Is it possible to implement the Encodable
and Decodable
properties for UIColor
When I try to add a Decodable
extension I get an error
extension UIColor : Decodable {
public required init(from decoder: Decoder) throws {
self.init(red: 1, green: 1, blue: 1, alpha: 1)
}
}
error: ColorStuff.playground:98:21: error: initializer requirement 'init(from:)' can only be satisfied by a
required
initializer in the definition of non-final class 'UIColor' public required init(from decoder: Decoder) throws {
Am I missing something obvious here?
I have no issues with the Encodable
extension - it seems its a Decodable
issue.
The error message implies to me that I cannot do this due to not having access to the UIColor
class definition
You cannot make UIColor
conform to Decodable
in an extension because of the error given by the compiler.
One solution is to make a Codable
wrapper type and use that instead.
Since UIColor
already conforms to NSCoding
, let's just write a generic type so we can encode and decode anything that conforms to NSCoding
.
import UIKit
struct WrapperOfNSCoding<Wrapped>: Codable where Wrapped: NSCoding {
var wrapped: Wrapped
init(_ wrapped: Wrapped) { self.wrapped = wrapped }
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
guard let object = NSKeyedUnarchiver.unarchiveObject(with: data) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "failed to unarchive an object")
}
guard let wrapped = object as? Wrapped else {
throw DecodingError.typeMismatch(Wrapped.self, DecodingError.Context(codingPath: container.codingPath, debugDescription: "unarchived object type was \(type(of: object))"))
}
self.wrapped = wrapped
}
func encode(to encoder: Encoder) throws {
let data = NSKeyedArchiver.archivedData(withRootObject: wrapped)
var container = try encoder.singleValueContainer()
try container.encode(data)
}
}
let colors = [UIColor.red, UIColor.brown]
print(colors)
let jsonData = try! JSONEncoder().encode(colors.map({ WrapperOfNSCoding($0) }))
let colors2 = try! JSONDecoder().decode([WrapperOfNSCoding<UIColor>].self, from: jsonData).map({ $0.wrapped })
print(colors2)