How to conform an ObservableObject to the Codable protocols?
Add the init()
and encode()
methods to your class:
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
name = try values.decode(String.self, forKey: .name)
ingredients = try values.decode([Ingredient].self, forKey: .ingredients)
numberOfPeople = try values.decode(Int.self, forKey: .numberOfPeople)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(ingredients, forKey: .ingredients)
try container.encode(numberOfPeople, forKey: .numberOfPeople)
}
How to conform a custom object to Codable
Other than not conforming the Skill
and SkillButton
classes to Codable
, the underlying problem you have is down to the @published
property wrapper, as this stops the auto-synthesis of Codable conformance.
Rather than writing out the solution, can I refer you to this Hacking with Swift link: https://www.hackingwithswift.com/books/ios-swiftui/adding-codable-conformance-for-published-properties
You will also have to conform Color
to Codable
yourself as (unless it's changed in recent updates?) it isn't Codable
out of the box.
How can I make my model conform to codable and save it locally on every change?
I like very much pawello2222's detailed response. If you continue to develop on iOS, you will eventually need to learn CoreData.
That said, if you don't have time to do that for your current project, personally, I would recommend using RealmSwift and creating Realm Parent, Child and Puppet objects. It would take you an hour to learn how to install and use RealmSwift.
Here's a start:
import RealmSwift
class Puppet: Object {
@objc dynamic var id = UUID()
@objc dynamic var name: String = ""
}
But if you want to save a small number of objects you could use UserDefaults - none of your objects are using unsupported types, so just declare them as codable and let the compiler do the conversion automatically.
Conforming class to codable protocol in swift4
It is quite straightforward. /p>
Codable conformance at @Published array of a codable-conformed Struct
/*
Cannot automatically synthesize 'Encodable' because 'Published<[User]>'
does not conform to 'Encodable' @Published var userArray = [User]()
*/
// Published declaration
@propertyWrapper struct Published<Value> { ... }
Published don't conform to Codable or any common protocol in Foundation currently
Trying to make Published
conform to Codeable
resulting error below:
/*
Implementation of 'Decodable' cannot be
automatically synthesized in an extension in a different file to the type
*/
extension Published: Codable where Value: Codable {}
Type 'Model' does not conform to protocol 'Decodable'/Encodable
A @Published
property with type, let's say, String
, are of type Published<String>
. And apparently, that type is not Codable
.
You can solve this by writing custom encode and decode functions. That's not difficult; it's only some extra lines of code. Please see the documentation on Codable
for some examples.
Here is an example for your case:
class TimeModel: Codable, Identifiable, ObservableObject {
@Published var id: UUID = UUID()
@Published var nome : String
@Published var time : String
func aggiornaUI() {
DispatchQueue.main.async {
self.objectWillChange.send()
}
}
init(nome: String, time: String) {
self.nome = nome
self.time = time
}
enum CodingKeys: String, CodingKey {
case id
case nome
case time
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(nome, forKey: .nome)
try container.encode(time, forKey: .time)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
nome = try container.decode(String.self, forKey: .nome)
time = try container.decode(String.self, forKey: .time)
}
}
This should work, but I can't really test it because I don't know the rest of your code.
Protocol type cannot conform to protocol because only concrete types can conform to protocols
Rather than protocols use generics.
Declare a simple function
func decodeStickers<T : Decodable>(from data : Data) throws -> T
{
return try JSONDecoder().decode(T.self, from: data)
}
T
can be a single object as well as an array.
In your structs drop the Sticker
protocol. You can also delete Equatable
because it's getting synthesized in structs.
public struct StickerString : Codable {
let fontName: String
let character: String
}
public struct StickerBitmap : Codable {
let imageName: String
}
To decode one of the sticker types annotate the type
let imageStickers = """
[{"imageName":"Foo"},{"imageName":"Bar"}]
"""
let stickerData = Data(imageStickers.utf8)
let recentStickers : [StickerBitmap] = try! decodeStickers(from: stickerData)
print(recentStickers.first?.imageName)
and
let stringSticker = """
{"fontName":"Times","character":"}
"""
let stickerData = Data(stringSticker.utf8)
let sticker : StickerString = try! decodeStickers(from: stickerData)
print(sticker.character)
To decode an array of StickerString
and StickerBitmap
types declare a wrapper enum with associated values
enum Sticker: Codable {
case string(StickerString)
case image(StickerBitmap)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let stringData = try container.decode(StickerString.self)
self = .string(stringData)
} catch DecodingError.keyNotFound {
let imageData = try container.decode(StickerBitmap.self)
self = .image(imageData)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let string) : try container.encode(string)
case .image(let image) : try container.encode(image)
}
}
}
Then you can decode
let stickers = """
[{"imageName":"Foo"},{"imageName":"Bar"}, {"fontName":"Times","character":"}]
"""
let stickerData = Data(stickers.utf8)
let recentStickers = try! JSONDecoder().decode([Sticker].self, from: stickerData)
print(recentStickers)
In a table view just switch
on the enum
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sticker = stickers[indexPath.row]
switch sticker {
case .string(let stringSticker):
let cell = tableView.dequeueReusableCell(withCellIdentifier: "StringStickerCell", for: indexPath) as! StringStickerCell
// update UI
return cell
case .image(let imageSticker):
let cell = tableView.dequeueReusableCell(withCellIdentifier: "ImageStickerCell", for: indexPath) as! ImageStickerCell
// update UI
return cell
}
}
Related Topics
How to Perform Face Detection in Swift
Bringing iOS Frameworks Through Carthage in Xcode 12.0
Swift, Error Exc_Breakpoint (Code=1, Subcode=0X100695474)
Swift Language Statically or Dynamically Dispatched
Skspritenode Gets Hidden Below Parent Node
How to Get the Url from Webview in Swift
Reading Data from Excel Document in a Swift App
Swiftui Classes That Conforms Observableobject Should Be Singleton
How to Get the Scnvector3 Position of the Camera in Relation to It's Direction Arkit Swift
Swift 3 Closure Overload Resolution
Sharing Highscore with Social Media
Type of Expression Is Ambiguous Without More Context in Xcode 11
No More 'Private Init' in Swift
Why 'Self.Self' Compiles and Run in Swift
Differences Between Filtering Realm with Nspredicate and Block
Make Nstextfield in Nstablecellview Firstresponder()