I have this codable struct:
struct Foo: Codable {
let rect: CGRect
let image: UIImage
init(rect: CGRect, image: UIImage) {
self.rect = rect
self.image = image
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(rect, forKey: .rect)
try container.encode(UIImagePNGRepresentation(image), forKey: .image)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
rect = try container.decode(CGRect.self, forKey: .rect)
image = try UIImage(data: container.decode(Data.self, forKey: .image))!
}
enum CodingKeys : CodingKey {
case rect
case image
}
}
Now I create a Foo
and attempt to encode it and decode it:
let image = UIImage(named: "my_image")
let foo = Foo(rect: .zero, image: image!)
let encoder = PropertyListEncoder()
let data = try! encoder.encode(foo)
let decoder = PropertyListDecoder()
try! decoder.decode(Foo.self, from: data)
Now at the decode
line, this error occurs:
Swift.DecodingError.typeMismatch(Foundation.Data, Swift.DecodingError.Context(codingPath: [__lldb_expr_244.Foo.CodingKeys.image], debugDescription: "Expected to decode Data but found an array instead.", underlyingError: nil))
Apparently the decoder found an array while trying to decode the image data. Why does this happen? It seems that somehow the Data
becomes an array. I am very confused because
Data() is Codable
evaluates to true, so the coders should theoretically be able to en/decode a Data
. Why does it become an array?
Edit: I'm using Xcode 9.1
Here is my_image
:
I'm sure this is a bug, it seems like the issue is during the encoding process, when encoding the UIImagePNGRepresentation
data, which is returned as Optional
things go really wrong, if you change the encode
method to force the data like this:
container.encode(UIImagePNGRepresentation(image)!, forKey: .image)
instead of:
container.encode(UIImagePNGRepresentation(image), forKey: .image)
Things will work both in Xcode 9.1 and 9.2, if you leave the data optional as it is, the encoding will work only on 9.2 and fail 9.1, for some reason the image data size will be doubled on 9.1.
I'm pretty sure it's this bug report on swift.org
Which also mentions that it causes memory usage double the size of the data, which is what I'm seeing on my local setup.
So the easy way around is to force the UIImagePNGRepresentation(..)
data or upgrade to 9.2 where this bug is fixed.
Hope this answered your question, very interesting find indeed!