iosjsonswiftcoinmarketcap

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.


Solution

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