1

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.

12
  • Why not just have the data's value always be an array of a single dictionary? Then the type is always an array, and if there is no data, it will simply be an empty array. If there is data, then you can just access the first element in data's array, and it will be a dictionary? Commented Oct 1, 2019 at 20:53
  • @DavidChopin i dont write the services. Back end developer is another developer. Commented Oct 1, 2019 at 20:56
  • Is the value only an array when there is no data returned? Commented Oct 1, 2019 at 20:57
  • 1
    @DavidChopin i can tell the backend developer to correct the json format. But i want to find solution for this problem :) Commented Oct 1, 2019 at 21:02
  • 1
    :) That's a good objection. I was just trying to keep it very simple. Then writing init(from:) is a better approach. Commented Oct 1, 2019 at 21:21

1 Answer 1

1

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) }
Sign up to request clarification or add additional context in comments.

4 Comments

thank you for this post. i am trying understand and use it :)
I do not understand anything. it is difficult
status can be "success" or "error", in case of success the user is decoded and attached to the success case otherwise the failure case is returned.
@NicatGüliyev I recommend you read the Swift Language Guide. It has an entire section on enums that I would recommend you check out. Seriously, it's a great book. If I were running a team, I wouldn't let anyone touch prod code before going through it cover to cover. Otherwise we're trying to compose a song without knowing what notes are available to us.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.