Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Codable for UIColor

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

like image 1000
Jeef Avatar asked Jun 15 '26 17:06

Jeef


1 Answers

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)
like image 157
rob mayoff Avatar answered Jun 17 '26 07:06

rob mayoff