Search code examples
swiftswiftuicodableobservedobject

How to Initialize Codable ObservedObject


In SwiftUI, I'm trying to create a class that conforms Codable (so I can save it in @AppStorage), and has @Published properties so it can be passed as an EnvironmentObject.

This tutorial gave me the following example:

class User: ObservableObject, Codable {
    
    enum CodingKeys: CodingKey {
        case name
    }
    
    @Published var name = "Bob Smith"
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
    }
    
    func encode(to encoder: Encoder) throws {
        ...
    }
}

I'm wondering how I would go about passing a String into the class initializer. (i.e. var user = User("Bob Smith")

Unless I'm mistaken, required means that all instances & subclasses must use that initializer. Given that name is already being assigned in the existing initializer, how would I go about passing a custom name into the initializer of this class?


Solution

  • Yes required means that all subclasses must implement the init and in this case it's a requirement because of the conformance to Codable.

    But this doesn't mean you can't add other initialisers so that you can create instances of your class from other type of data than JSON

    So simply add wathever init you need

    init(name: String) {
        self.name = name
    }
    
    let user = User(name: "Joe")
    

    And for a sub class you must add an init(from:) and you might add another custom init if you want to, here is an example

    class SuperUser: User {
        var likesBananas: Bool
    
        enum CodingKeys: CodingKey {
            case likesBananas
        }
    
        required init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            likesBananas = try container.decode(Bool.self, forKey: .likesBananas)
    
            try super.init(from: decoder)
        }
    
        init(name: String, likesBananas: Bool) {
            self.likesBananas = likesBananas
            super.init(name: name)
        }
    
        override func encode(to encoder: Encoder) throws {...}
    }