Search code examples
swiftcombinecodable

Why does @Published attribute cause this error: Type 'Booking' does not conform to protocol 'Decodable'


I ran into a weird problem. Just adding the attribute @Published causes this error: "Type 'Booking' does not conform to protocol 'Decodable'"

THIS compiles fine:

class Booking: ObservableObject, Codable {
    private (set) var module    : Module   
    private (set) var lecturer  : Lecturer? = nil
    
    init(withModul module : Module) {
        self.module = module
    }
}

WHILE here comes the error:

class Booking: ObservableObject, Codable {
    private (set) var module : Module   
    @Published private (set) var lecturer : Lecturer? = nil

    init(withModul module : Module) {
        self.module = module
    }
}

And of course type 'Lecturer' and 'Module' (which are structs) conform both to 'Codable' and also all its properties.

Never had that problem before, any suggestions.


Solution

  • Because you've added the @Published property wrapper to lecturer, the type of that property is now Published<Lecturer> rather than Lecturer. Published doesn't conform to Codable even if it's wrapped Value conforms to Codable.

    If you don't want the wrapped property to be part of the encoding/decoding, you can simply omit it from the Codable conformance by creating a CodingKey conformant enum for Booking and omitting the lecturer property from there.

    class Booking: ObservableObject, Codable {
        private (set) var module : Module
        @Published private (set) var lecturer : Lecturer? = nil
    
        init(withModul module : Module) {
            self.module = module
        }
    
        enum CodingKeys: String, CodingKey {
          case module
        }
    }
    

    Alternatively, if you do want encode/decode lecturer, you need to manually implement the Codable conformance.

    class Booking: ObservableObject, Codable {
      private (set) var module : Module
      @Published private (set) var lecturer : Lecturer? = nil
    
      init(withModul module : Module) {
        self.module = module
      }
    
      enum CodingKeys: String, CodingKey {
        case module
        case lecturer
      }
    
      required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.lecturer = try container.decodeIfPresent(Lecturer.self, forKey: .lecturer)
        self.module = try container.decode(Module.self, forKey: .module)
      }
    
      func encode(to encoder: Encoder) throws {
        try lecturer?.encode(to: encoder)
        try module.encode(to: encoder)
      }
    }