Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to conform UIImage to Codable?

Swift 4 has Codable and it's awesome. But UIImage does not conform to it by default. How can we do that?

I tried with singleValueContainer and unkeyedContainer

extension UIImage: Codable {   // 'required' initializer must be declared directly in class 'UIImage' (not in an extension)   public required init(from decoder: Decoder) throws {     let container = try decoder.singleValueContainer()     let data = try container.decode(Data.self)     guard let image = UIImage(data: data) else {       throw MyError.decodingFailed     }      // A non-failable initializer cannot delegate to failable initializer 'init(data:)' written with 'init?'     self.init(data: data)   }    public func encode(to encoder: Encoder) throws {     var container = encoder.singleValueContainer()     guard let data = UIImagePNGRepresentation(self) else {       return     }      try container.encode(data)   } } 

I get 2 errors

  1. 'required' initializer must be declared directly in class 'UIImage' (not in an extension)
  2. A non-failable initializer cannot delegate to failable initializer 'init(data:)' written with 'init?'

A workaround is to use wrapper. But are there any other ways?

like image 752
onmyway133 Avatar asked Sep 13 '17 12:09

onmyway133


People also ask

How do you make a UIImage conform to Codable?

Properly the easiest way is to just make it Data instead of UIImage : public struct SomeImage: Codable { public let photo: Data public init(photo: UIImage) { self. photo = photo. pngData()! } }

Does UIImage conform to Codable?

I believe that's happening because UIImage doesn't automatically conform to Codable, hence my question. Also, I'm decoding and encoding an array of UserImage because I'm trying to decode and encode the images property, which is an array of UserImage.

Can a protocol conform to Codable?

Codable is Encodable , but the compiler says it does not conform to it. This may sound strange, but it's because we are using Codable as a type, and the type Codable (not the protocol definition of Codable ) does not conform to Encodable .

Can a class conform to Codable?

If all the properties of a type already conform to Codable , then the type itself can conform to Codable with no extra work – Swift will synthesize the code required to archive and unarchive your type as needed.


1 Answers

A solution: roll your own wrapper class conforming to Codable.

One solution, since extensions to UIImage are out, is to wrap the image in a new class you own. Otherwise, your attempt is basically straight on. I saw this done beautifully in a caching framework by Hyper Interactive called, well, Cache.

Though you'll need to visit the library to drill down into the dependencies, you can get the idea from looking at their ImageWrapper class, which is built to be used like so:

let wrapper = ImageWrapper(image: starIconImage) try? theCache.setObject(wrapper, forKey: "star")  let iconWrapper = try? theCache.object(ofType: ImageWrapper.self, forKey: "star") let icon = iconWrapper.image 

Here is their wrapper class:

// Swift 4.0 public struct ImageWrapper: Codable {   public let image: Image    public enum CodingKeys: String, CodingKey {     case image   }    // Image is a standard UI/NSImage conditional typealias   public init(image: Image) {     self.image = image   }    public init(from decoder: Decoder) throws {     let container = try decoder.container(keyedBy: CodingKeys.self)     let data = try container.decode(Data.self, forKey: CodingKeys.image)     guard let image = Image(data: data) else {       throw StorageError.decodingFailed     }      self.image = image   }    // cache_toData() wraps UIImagePNG/JPEGRepresentation around some conditional logic with some whipped cream and sprinkles.   public func encode(to encoder: Encoder) throws {     var container = encoder.container(keyedBy: CodingKeys.self)     guard let data = image.cache_toData() else {         throw StorageError.encodingFailed     }      try container.encode(data, forKey: CodingKeys.image)   } } 

I'd love to hear what you end up using.

UPDATE: It turns out the OP wrote the code that I referenced (the Swift 4.0 update to Cache) to solve the problem. The code deserves to be up here, of course, but I'll also leave my words unedited for the dramatic irony of it all. :)

like image 152
AmitaiB Avatar answered Oct 06 '22 12:10

AmitaiB