Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make UIColor Codable

Tags:

struct Task: Codable {     var content: String     var deadline: Date     var color: UIColor ... } 

There are warnings saying "Type 'Task' does not conform to protocol 'Decodable'" and "Type 'Task' does not conform to protocol 'Encodable'". I searched and found that this is because UIColor does not conform to Codable. But I have no idea how to fix that. So...

How to make UIColor Codable?

like image 803
Lambdalex Avatar asked Jun 19 '18 12:06

Lambdalex


People also ask

Can a class conform to Codable?

Codable protocol The Swift standard library contains types like String , Int , Double , Date , Data , and URL that already conform to Codable . If we create a custom struct , enum , or class , we can conform it to the Codable protocol and use the already existing types without implementing any methods.

Is SwiftUI color Codable?

SwiftUI Color type doesn't conform to Codable by default. If we need to save it to disk as part of a Codable type or on its own we need to define how it should be encoded ourselves. For encoding purposes we can divide SwiftUI colors into 2 types: constant and dynamic colors.

Can a protocol be Codable Swift?

Codable is the combined protocol of Swift's Decodable and Encodable protocols. Together they provide standard methods of decoding data for custom types and encoding data to be saved or transferred.

What is the use of Codable?

Codable is a type alias for the Encodable and Decodable protocols. When you use Codable as a type or a generic constraint, it matches any type that conforms to both protocols.


1 Answers

If you care only about the 4 color components this is a simple solution using a wrapper struct

struct Color : Codable {     var red : CGFloat = 0.0, green: CGFloat = 0.0, blue: CGFloat = 0.0, alpha: CGFloat = 0.0          var uiColor : UIColor {         return UIColor(red: red, green: green, blue: blue, alpha: alpha)     }          init(uiColor : UIColor) {         uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)     } } 

In this case you have to write a custom initializer to convert the 4 color components from Color to UIColor and vice versa.

struct Task: Codable {          private enum CodingKeys: String, CodingKey { case content, deadline, color }          var content: String     var deadline: Date     var color : UIColor          init(content: String, deadline: Date, color : UIColor) {         self.content = content         self.deadline = deadline         self.color = color     }         init(from decoder: Decoder) throws {         let container = try decoder.container(keyedBy: CodingKeys.self)         content = try container.decode(String.self, forKey: .content)         deadline = try container.decode(Date.self, forKey: .deadline)         color = try container.decode(Color.self, forKey: .color).uiColor     }          public func encode(to encoder: Encoder) throws {         var container = encoder.container(keyedBy: CodingKeys.self)         try container.encode(content, forKey: .content)         try container.encode(deadline, forKey: .deadline)         try container.encode(Color(uiColor: color), forKey: .color)     } } 

Now you can encode and decode UIColor

let task = Task(content: "Foo", deadline: Date(), color: .orange) do {     let data = try JSONEncoder().encode(task)     print(String(data: data, encoding: .utf8)!)     let newTask = try JSONDecoder().decode(Task.self, from: data)     print(newTask) } catch {  print(error) } 

A smart alternative for Swift 5.1 and higher is a property wrapper

@propertyWrapper struct CodableColor {     var wrappedValue: UIColor }  extension CodableColor: Codable {     init(from decoder: Decoder) throws {         let container = try decoder.singleValueContainer()         let data = try container.decode(Data.self)         guard let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) else {             throw DecodingError.dataCorruptedError(                 in: container,                 debugDescription: "Invalid color"             )         }         wrappedValue = color     }      func encode(to encoder: Encoder) throws {         var container = encoder.singleValueContainer()         let data = try NSKeyedArchiver.archivedData(withRootObject: wrappedValue, requiringSecureCoding: true)         try container.encode(data)     } } 

and mark the property with @CodableColor

struct Task: Codable {     var content: String     var deadline: Date     @CodableColor var color: UIColor ... } 
like image 149
vadian Avatar answered Nov 12 '22 22:11

vadian