1

I've been struggling with this for a while. I have a JSON that I get from an API call, but it has a key which can either be false ou return a value if it's true.

like this:

{
  "id": 550,
  "favorite": true,
  "rated": {
    "value": 8
  },
  "watchlist": false
}

or this:

{
  "id": 550,
  "favorite": true,
  "rated": false,
  "watchlist": false
}

I tried to decode it like this:

struct AccountState: Decodable {
    var id: Int?
    var favorite: Bool?
    var rated: CustomValue
    var watchlist: Bool?
}

struct RatingValue: Decodable {
    var value: Double?
}

enum CustomValue: Decodable {
    case bool(Bool)
    case rating(RatingValue)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let bool = try? container.decode(Bool.self) {
            self = .bool(bool)
        } else if let rating = try? container.decode(RatingValue.self) {
            self = .rating(rating)
        } else {
            let context = DecodingError.Context(codingPath: container.codingPath, debugDescription: "Unknown type")
        throw DecodingError.dataCorrupted(context)
        }
    }
}

in ViewController:

func dowloadAndDecodeData() {
 let url...
 ...

 let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            guard let accountState = try? decoder.decode(AccountState.self, from: data) else {
                print("error")
                return
            }
 print(accountState)
}

In the console I can see the JSON content is correctly parsed (returning either false or value if it exists).

The question is: how can I access this value from code? Since "rated" is of type "CustomValue", I can't just do accountState.rated.value like I would normally use.

Herlid
  • 105
  • 1
  • 9

2 Answers2

1

To access something like CustomValue (enum cases with associated value), you need to use switch-case or if-case.

    switch accountState.rated {
    case .rating(let ratingValue):
        print(ratingValue.value)
    case .bool(let boolValue):
        print(boolValue);
    }

Or else, you can define a simple extension for CustomValue:

extension CustomValue {
    var value: Double? {
        switch self {
        case .rating(let ratingValue):
            return ratingValue.value
        case .bool: //### in case .bool, return nil
            return nil
        }
    }
}

And you can simple access it as:

    if let value = accountState.rated.value {
        print(value)
    }
OOPer
  • 47,149
  • 6
  • 107
  • 142
0

I faced same issue, I have made property called Any. So i can cast it to needed datatype.

check my answer here. How to use Any in Codable Type

I have not added bool type but it will 100 % solve ur issues, Let me know if you need further help

Read my answer before adding below property in your CustomValue class.

 var any:Any{
        get{
            switch self {
            case .bool(let value):
                return value
            }
        }
    }
indrajit
  • 303
  • 1
  • 14