Search code examples
swiftcodabledecodable

Swift Codable how to use Any type?


When I try to access the value of "value" for example to use it in a label.text, I get an error

Cannot assign value of type 'MyValue?' to type 'String?'

When I print the value to the terminal, it says ...

unknown context at 0x109d06188).MyValue.string...

How can solve this problem?

struct Root: Codable {
    let description,id: String
    let group,groupDescription: String?
    let name: String
    let value: MyValue

    enum CodingKeys: String, CodingKey {
        case description = "Description"
        case group = "Group"
        case groupDescription = "GroupDescription"
        case id = "Id"
        case name = "Name"
        case value = "Value"
    }
}

enum MyValue: Codable {
    case string(String)
    case innerItem(InnerItem)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        if let x = try? container.decode(InnerItem.self) {
            self = .innerItem(x)
            return
        }
        throw DecodingError.typeMismatch(MyValue.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for MyValue"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .string(let x):
            try container.encode(x)
        case .innerItem(let x):
            try container.encode(x)
        }
    }
}

Solution

  • You can get string values for your label by conforming to rawRepresentable protocol:

    enum MyValue: Codable, RawRepresentable {
    
    
    var rawValue: String {
        switch self {
        case .string(let stringVal):
            return stringVal
        case .innerItem(let myVal):
            return String(describing: myVal)
        }
    }
    
    typealias RawValue = String
    
    init?(rawValue: String) {
        return nil
    }
    
    
    
    case string(String)
    case innerItem(InnerItem)
    
    }
    
    let myVal = MyValue.string("testString")
    var strVal: String = myVal.rawValue // testString