How to make `ObservableObject` with `@Published` properties `Codable`?

256 Views Asked by At

My codable observable Thing compiles:

class Thing: Codable, ObservableObject {
    var feature: String
}

Wrapping feature in @Published though doesn’t:

class Thing: Codable, ObservableObject {
    @Published var feature: String
}
 Class 'Thing' has no initializers
 Type 'Thing' does not conform to protocol 'Decodable'
 Type 'Thing' does not conform to protocol 'Encodable'

Apparently Codable conformance can’t be synthesized anymore because @Published doesn’t know how to encode/decode its wrappedValue (because it doesn’t conform to Codable even if its wrapped value does)?

Okay, so I’ll let it know how to do it!

extension Published: Codable where Value: Codable {
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(wrappedValue) //  'wrappedValue' is unavailable: @Published is only available on properties of classes
    }

    public init(from decoder: Decoder) throws {
        var container = try decoder.singleValueContainer()
        wrappedValue = try container.decode(Value.self) //  'wrappedValue' is unavailable: @Published is only available on properties of classes
    }
}

So sorrow, much misery, many sadness!

How can I easily add back Codable synthesis (or similar) without defining encode(to:) and init(from:)?

1

There are 1 best solutions below

0
On

I don't know why you would want to do it, I think you should code the struct, not the published class. Something like this:

struct CodableStruct: Codable {
    var feature1: String = ""
    var feature2: Int = 0
}

class Thing: ObservableObject {
    @Published var features: CodableStruct = .init()
}