I've been doing some tests on my models to make sure they are equal when I encode them into JSON and then decode them back using JSONEncoder/Decoder
. However, one of my tests failed, and the culprit was UIImage
. I've made sure that no errors were thrown during the encoding/decoding process.
First of all, this is the test in question:
func testProfileImageCodable() throws {
let image = ProfileImage(UIImage(systemName: "applelogo")!)
try XCTAssertTrue(assertCodable(image))
}
Here's my "Codability" test, where I make sure that types are equal before and after encoding/decoding:
func assertCodable<T: Codable & Equatable>(
_ value: T,
decoder: JSONDecoder = .init(),
encoder: JSONEncoder = .init()
) throws -> Bool {
let encoded = try encoder.encode(value)
let decoded = try decoder.decode(T.self, from: encoded)
return value == decoded
}
Firstly, here's how I made UIImage
work with Codable
:
extension KeyedEncodingContainer {
mutating func encode(_ value: UIImage, forKey key: Key) throws {
guard let data = value.pngData() else {
throw EncodingError.invalidValue(
value,
EncodingError.Context(codingPath: [key],
debugDescription: "Failed convert UIImage to data")
)
}
try encode(data, forKey: key)
}
}
extension KeyedDecodingContainer {
func decode(_ type: UIImage.Type, forKey key: Key) throws -> UIImage {
let imageData = try decode(Data.self, forKey: key)
if let image = UIImage(data: imageData) {
return image
} else {
throw DecodingError.dataCorrupted(
DecodingError.Context(codingPath: [key],
debugDescription: "Failed load UIImage from decoded data")
)
}
}
}
The UIImage
lives in a ProfileImage
type, so conforming it to Codable
looks like this:
extension ProfileImage: Codable {
enum CodingKeys: CodingKey {
case image
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.image = try container.decode(UIImage.self, forKey: .image)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.image, forKey: .image)
}
}
Furthermore, ProfileImage
's Equatable
conformance uses isEqual(_:)
on the UIImage
property, which they say is "the only reliable way to determine whether two images contain the same image data."
Yet, my test still fails, and I'm not sure why. Any help would be greatly appreciated.
func testProfileImageCodable() throws { let testImage = UIImage(systemName: "applelogo")! let encoded = try JSONEncoder().encode(ProfileImage(testImage)) let decoded = try JSONDecoder().decode(ProfileImage.self, from: encoded) XCTAssertTrue(decoded.image.pngData() == testImage.pngData()) }
and it still fails. Maybe it's a problem with how I implemented UIImage's encoding/decoding? (sorry for the formatting, not being to add full code to SO comments is a pain) – Izard