Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

struct for nested dictionary API in swift

I'm trying to import JSON data from the v2 of coinmarketcap API. I had it working with v1 as it was an array, however the new version is a dictionary and I can't quite get my struct correct. The API im using is : https://api.coinmarketcap.com/v2/ticker/?convert=AUD

My struct is set up as below:

struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
    case id = "rank", symbol, name, priceAUD = "quotes"
}
var id: String
var symbol : String
var name : String
var priceAUD : quoteStruct

}

struct quoteStruct{
    let aud : priceStruct
}

struct priceStruct{
    let price : String
}

My code for fetching the data is:

var coins = [Coin]()

func getCoinData() {
    let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
    let url = URL(string: jsonURL)
    
    URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
        guard let data = data else { return }
        do {
            self.coins = try JSONDecoder().decode([Coin].self, from: data)
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
            
        } catch {
            print("Error is : \n\(error)")
        }
        }.resume()
}

My code for fetching the data I have used the same as previously which worked with v1 of the API, however I don't think I made my struct correctly.

like image 357
Sean Slack Avatar asked Feb 17 '26 15:02

Sean Slack


2 Answers

Your response Changed i try to configure it by converting it to array of dictionary you will need to change quotes to be [String:priceStruct]

struct Coin: Decodable {
    private enum CodingKeys: String, CodingKey {
        case id,rank,symbol, name, priceAUD = "quotes"
    }
    var id: Int
    var rank: Int
    var symbol : String
    var name : String
    var priceAUD : [String: priceStruct]

}

struct priceStruct : Decodable{
    let price : Double
}

 func getCoinData() {
         var coins = [Coin]()
        let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
        let url = URL(string: jsonURL)

        URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
            guard let data = data else { return }
            do {
                 if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], let resultData = json["data"]  as? [String:Any] {

                     let dataObject = try JSONSerialization.data(withJSONObject: resultData.values.map({$0})  , options: .prettyPrinted)
                    coins = try JSONDecoder().decode([Coin].self, from: dataObject)
                    print(coins.count)

                  }



            } catch {
                print("Error is : \n\(error)")
            }
            }.resume()
    }
like image 190
Abdelahad Darwish Avatar answered Feb 20 '26 03:02

Abdelahad Darwish


You response in data parameter should be an array rather than a dictionary. You will not be able to iterate a dictionary over undefined keys. It would be good to get your response of API updated first.

But, If you wish to continue with the existing API response, first you need to convert your response in an array and use your Decodable structs as:

struct Coin: Decodable {
    var id: String
    var symbol : String
    var name : String
    var priceAUD : QuoteStruct

    private enum CodingKeys: String, CodingKey {
        case id = "rank", symbol, name, priceAUD = "quotes"
    }
}

struct QuoteStruct: Decodable {
    let aud : PriceStruct
}

struct PriceStruct: Decodable {
    let price : String
}

Update your data parsing in API block as:

    guard let responseData = data else { return }
    do {
        let json = try? JSONSerialization.jsonObject(with: responseData, options: [])
        if let jsonData = json as? [String: Any], let dataObject = jsonData["data"] as? [Int: Any] {
            let coinArray = dataObject.map { $0.1 }

            if let jsonData = try? JSONSerialization.data(withJSONObject: coinArray, options: .prettyPrinted) {
                coins = try JSONDecoder().decode([Coin].self, from: jsonData)
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
        }
    } catch {
        print("Error is : \n\(error)")
    }
like image 44
Ankit Jayaswal Avatar answered Feb 20 '26 04:02

Ankit Jayaswal



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!