Learning Lee
Learning Lee

Reputation: 63

How do i decode an array of dictioniaries using codable

I am trying to call an API that has an array of dictionaries: I Got the error below and not sure how I can fix. This is my struct model

struct Countries : Codable {
    var countries : [Country]
}

struct Country : Codable {
    var Country : String
    var NewConfirmed : Int
    var TotalConfirmed : Int
    var NewDeaths : Int
    var TotalDeaths : Int
    var NewRecovered : Int
    var TotalRecovered : Int
    var CountryCode : String
}

This is my networking code :

 class Networking {
    func response (url: String  , completion: @escaping (Countries) -> ()) {
        guard let url = URL(string: url) else {return}
        URLSession.shared.dataTask(with: url, completionHandler: { (data , response , error ) in
            self.urlCompletionHandler(data: data, response: response, error: error, completion: completion)
            }).resume()
    }

    func urlCompletionHandler (data: Data?  , response : URLResponse? , error : Error? , completion: (Countries) -> ()) {
        guard let data = data else {return}
        do {
            let jsondecoder = JSONDecoder()
            let countries = try jsondecoder.decode(Countries.self, from: data)
            completion(countries)
        } catch {
            print("Error \(error)")
        }
    }
}

And this is my call in the controller :

  func response () {
                newtorkhanler.response(url: UrlPathSingleTon.urlsingleton.shared()) { (countried : (Countries)) in
                    self.countrizs = countried.countries
                    DispatchQueue.main.async {
                        self.tableview.reloadData()
                    }
                }

I want to populate an array of Country that I made it as an array variable inside Countries struct. My plan is to decode the Countries and use its array variable of Country that I named countries and set to the countrizs array of Country inside response function. I get this error that is specific to the countries variable inside the Countries struct :

Error keyNotFound(CodingKeys(stringValue: "countries", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"countries\", intValue: nil) (\"countries\").", underlyingError: nil))

And this is the API json format: the array JSON data is in the attached pic:

json data pic

{
    "Countries": [
         {
            "Country": "Afghanistan",
            "CountryCode": "AF",
            "Slug": "afghanistan",
            "NewConfirmed": 787,
            "TotalConfirmed": 18054,
            "NewDeaths": 6,
            "TotalDeaths": 300,
            "NewRecovered": 63,
            "TotalRecovered": 1585,
            "Date": "2020-06-05T17:51:15Z"
        },
        {
            "Country": "Albania",
            "CountryCode": "AL",
            "Slug": "albania",
            "NewConfirmed": 13,
            "TotalConfirmed": 1197,
            "NewDeaths": 0,
            "TotalDeaths": 33,
            "NewRecovered": 0,
            "TotalRecovered": 898,
            "Date": "2020-06-05T17:51:15Z"
        }
    ]
}

Upvotes: 0

Views: 188

Answers (2)

Frankenstein
Frankenstein

Reputation: 16341

To fix this issue you need to modify your property from countries to Countries. Your keys should match the keys in the JSON.

struct Countries : Codable {
    var Countries : [Country]
}

Or you could make the property name lowercased and the struct uppercased Swift style like this:

struct Countries : Codable {
    var countries : [Country]
    enum CodingKeys: String, CodingKey {
         case countries = "Countries"
    }
}

Upvotes: 1

kid_x
kid_x

Reputation: 1475

If your var names vary from the names of the keys in your json, you can define your coding keys explicitly so they match up (otherwise, when all properties are already Decodable, your coding keys will be synthesized).

struct Countries : Codable {
    var countries : [Country]

    enum CodingKeys: String, CodingKey {
         case countries = "Countries"
    }
}

Note your cases must match up to the names of the vars you are trying to decode. The compiler will enforce this, and your object won't conform to Decodable (or Encodable) while you have properties without matching cases in your CodingKeys enum.

This way, you can maintain your naming conventions.

Upvotes: 1

Related Questions