Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I decode response data if data is nested in Swift

I've got a problem with decoding response data. Here is my request function

  @IBAction func onGetCities(_ sender: UIButton) {

    guard let url = URL(string: "http://somelink.com/city-list") else { return }

    var request = URLRequest(url: url)
    request.httpMethod = "GET"

    let session = URLSession.shared
    let task = session.dataTask(with: request) { (data, response, error) in


        print(JSON(data))
        guard let data = data else { return }
        do{
            let cities = try JSONDecoder().decode([City].self, from: data)
            print(cities)
        }catch{

        }
    }
    task.resume()
}

And City struct

struct City: Decodable {
    let id: Int
    let city: String
}

Here is the response data, I want to decode "items"

{
"offset": 0,
"limit": 10,
"items": [
  {id: 0, name: "City name"},
  {id: 1, name: "City name1"},
  .....
]

}
like image 371
Aidogdy Shahyrov Avatar asked Oct 24 '25 09:10

Aidogdy Shahyrov


1 Answers

You need to have nested structures that mirror your nested JSON:

struct City: Decodable {
    let id: Int
    let city: String
}

struct ResponseObject: Decodable {
    let items: [City]
    let offset: Int
    let limit: Int
}

And then:

do {
    let result = try JSONDecoder().decode(ResponseObject.self, from: data)
    print(result)

    let cities = result.items
    print(cities)
} catch {
    print(error)
}

Note, in your original example, you included a JSON key, updated_at which was the date represented in number of seconds since 1970 (standard UNIX representation). So, to decode that:

struct City: Decodable {
    let id: Int
    let city: String
}

struct ResponseObject: Decodable {
    let items: [City]
    let offset: Int
    let limit: Int
    let code: Int
    let updatedAt: Date
}

And then:

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
decoder.keyDecodingStrategy = .convertFromSnakeCase

do {
    let result = try decoder.decode(ResponseObject.self, from: data)
    print(result)

    let cities = result.items
    print(cities)
} catch {
    print(error)
}

Note, when you see a somewhat abstract key name, such as items, that means that this might be a response structure used for various endpoints. In that case, we might to make this a generic:

struct ItemsResponse<T: Decodable>: Decodable {
    let items: [T]
    let offset: Int
    let limit: Int
    let code: Int
    let updatedAt: Date
}

And then:

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
decoder.keyDecodingStrategy = .convertFromSnakeCase

do {
    let result = try decoder.decode(ItemsResponse<City>.self, from: data)
    print(result)

    let cities = result.items
    print(cities)
} catch {
    print(error)
}
like image 141
Rob Avatar answered Oct 27 '25 00:10

Rob