I have tried to implement the solution by Vadian here: Make UIColor Codable
But I am getting an error that I haven't been able to overcome.
Here is my implementation of the above mention solution:
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)
}
}
struct Tasting: Codable {
private enum CodingKeys: String, CodingKey { case id, title, color, textColor, notes }
var id: Int
var title: String
var color : UIColor
var textColor : UIColor
var notes: String
init(id: Int, title: String, color : UIColor, textColor : UIColor, notes: String) {
self.id = id
self.title = title
self.color = color
self.textColor = textColor
self.notes = notes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
title = try container.decode(String.self, forKey: .title)
color = try container.decode(Color.self, forKey: .color).uiColor
textColor = try container.decode(Color.self, forKey: .textColor).uiColor
notes = try container.decode(String.self, forKey: .notes)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(title, forKey: .title)
try container.encode(Color(uiColor: color), forKey: .color)
try container.encode(Color(uiColor: color), forKey: .textColor)
try container.encode(notes, forKey: .notes)
}
}
And here:
//Encodes UIColor so it can be saved
let tastings = [
Tasting(id: 0, title: "(Delete this row after you add your first tasting!)", color: .green, textColor: .black, notes: "Add notes here.")
]
do {
let data = try JSONEncoder().encode(tastings)
print(String(data: data, encoding: .utf8)!)
let newTastings = try JSONDecoder().decode(Tasting.self, from: data)
print("newTastings \(newTastings)")
} catch {
print("newTastings \(error)")
}
Then saving it to UserDefaults.
//Saves new brand to device memory
let savedTastings = tastings
UserDefaults.standard.set(savedTastings, forKey: "tastings")
These are the errors I am getting:
newTastings typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
2019-07-25 11:38:05.909711-0700 WhiskyTasting[10601:3581697] [User Defaults] Attempt to set a non-property-list object (
"WhiskyTasting.Tasting(id: 0, title: \"(Delete this row after you add your first tasting!)\", color: UIExtendedSRGBColorSpace 0 1 0 1, textColor: UIExtendedGrayColorSpace 0 1, notes: \"Add notes here.\")"
) as an NSUserDefaults/CFPreferences value for key tastings
2019-07-25 11:38:05.910207-0700 WhiskyTasting[10601:3581697] ***
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object (
"WhiskyTasting.Tasting(id: 0, title: \"(Delete this row after you add your first tasting!)\", color: UIExtendedSRGBColorSpace 0 1 0 1, textColor: UIExtendedGrayColorSpace 0 1, notes: \"Add notes here.\")"
) for key tastings'
I hope y'all see a simple typo that I'm missing. Been struggling with variations of this for a few days.
The errors are not related to my solution.
The first error tells you that the object is an array. Please read your code, tastings
is clearly an array.
So you have to decode an array
let newTastings = try JSONDecoder().decode([Tasting].self, from: data)
The second error tells you that in your struct is a type which is not property list compliant. This type is UIColor
. You cannot save Tasting
instances to UserDefaults
, but you can save JSON-/ or PropertyList-encoded Tasting
instances.
let data = try JSONEncoder().encode(tastings)
UserDefaults.standard.set(data, forKey: "tastings")