Nicat Güliyev
Nicat Güliyev

Reputation: 97

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.

Upvotes: 1

Views: 180

Answers (1)

vadian
vadian

Reputation: 285290

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) }

Upvotes: 1

Related Questions