Codable conformance at @Published array of a codable-conformed Struct

513 Views Asked by At

I am tinkering around with URLSession and was essentially building a super simple app to load a JSON file into ContentView sort of like a friends list from Facebook and wanted some clarity on not any errors I'm having, but rather, the internal workings of Swift's Codable protocol. Here are some code and explanations:

struct User: Identifiable, Codable {
    struct Friend : Identifiable, Codable {
        var name : String
        var id : String
    }

    var id : String
    var isActive: Bool
    var name : String
    var age: Int
    var company : String
    var address : String
    var about : String
    var registered : String
    
    var friends : [Friend]
    
    var checkIsActive: String {
        return self.isActive ? "" :""
    }
    
}

So to summarize above, I have a User struct which contains a bunch of properties that conform to Codable.

class UsersArrayClass: ObservableObject {
    @Published var userArray = [User]() 
}

However, I have another class, UsersArrayClass which creates a @Published var userArray of User struct objects. This class conforms to the @ObservableObject protocol, but of course, when I try to make it conform to Codable, it does not like that likely because of the @Published property wrapper being applied on the array itself... This is what is essentially confusing me though if the User struct has Codable conformance, why doesn't userArray which contains User objects automatically conform to Codable as well?

I was thinking that maybe loading all of this up into a Core Data model would solve my problems, but I still can't move on unless I understand what I'm missing out on here, so thanks in advance for any input.

2

There are 2 best solutions below

2
On BEST ANSWER
/*
 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 {}
0
On

It is hacky, but we can via extension add Codable conformance to Published despite lacking access to Published internals.

extension Published: Codable where Value: Codable {
    public func encode(to encoder: Encoder) throws {
        guard
            let storageValue =
                Mirror(reflecting: self).descendant("storage")
                .map(Mirror.init)?.children.first?.value,
            let value =
                storageValue as? Value
                ??
                (storageValue as? Publisher).map(Mirror.init)?
                .descendant("subject", "currentValue")
                as? Value
        else { fatalError("Failed to encode") }
        
        try value.encode(to: encoder)
    }
    
    public init(from decoder: Decoder) throws {
        self.init(initialValue: try .init(from: decoder))
    }
}

Quick check:

class User: ObservableObject, Codable {
    @Published var name = "Paul"
}

struct ContentView: View {
    @ObservedObject var user = User()
    var body: some View {
        let data = try? JSONEncoder().encode(user)
        let dataFromStr = """
                {
                    "name": "Smith"
                }
                """
            .data(using: .utf8)
        let decoded = try! JSONDecoder().decode(User.self, from: dataFromStr!)
        return
            VStack{
                Text(verbatim: String(data: data!, encoding: .utf8) ?? "encoding failed")
                Text(decoded.name)
            }
    }
}