Search code examples
jsonswiftstructdecodable

How to use decodable protocol with custom type values in Swift?


I have 2 types of response depending on my reuest: First one:

{
    "status": "success"
    "data": {
        "user_id": 2,
        "user_name": "John"      
    }
}

And second one is:

{
    "status": "error",
    "data": [],
}

I am using struct like that:

struct ValyutaListData:Decodable {
    let status: String? 
    let data: [String]?
}

But if response is first type response, then an error occured. Because In first Type response data is not array. It is Json object. Then i use structure like that:

struct ValyutaListData:Decodable {
    let status: String? 
    let data: Persondata?
}

struct Persondata: Decodable{
    let user_id: Int?
    let user_name: String?
}

If response is second type response, the error will be occured. What kind of of structure should use for dynamic type JSONs? Thanks.


Solution

  • One reasonable solution is an enum with associated type(s)

    struct User : Decodable {
        let userId: Int
        let userName: String
    }
    
    enum Result : Decodable {
        case success(User), failure
    
        enum CodingKeys: String, CodingKey { case status, data }
    
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            let status = try container.decode(String.self, forKey: .status)
            if status == "success" {
                let userData = try container.decode(User.self, forKey: .data)
                self = .success(userData)
            } else {
                self = .failure
            }
        }
    }
    

    And use it

    do {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        let result = try decoder.decode(Result.self, from: data)
        switch result {
          case .success(let user): print(user)
          case .failure: print("An error occurred")
        }
    } catch { print(error) }