Search code examples
swiftcoinmarketcap

Coinbase API parsing into Swift App returns incorrect formatting


I am using coinmarketcap api to fetch coin prices using the code down below. The data model Coin is also given below after the code as well as the JSON response. I get an error "The data couldn’t be read because it isn’t in the correct format." What should the correct formating look like?

'''

import Foundation
import SwiftUI
import Alamofire

class CryptoViewModel: ObservableObject {
    
    func fetchData() {
        let headers: HTTPHeaders = [
            "Accepts": "application/json",
              "X-CMC_PRO_API_KEY": "5dd693fc-6446-44c4-8aaa-75b1bfa4376f"
        ]
        
        AF.request("https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest", headers: headers).response { response in
            guard let data = response.data else { return }
            
            do {
                let coins = try JSONDecoder().decode([Coin].self, from: data)
                print(coins)
            }
            catch {
                print(error.localizedDescription)
            }
            
        }
        
    }
    
}

'''

'''

import SwiftUI

struct Coin: Decodable {
    
    var slug: String?
    var symbol: String?
    
    enum CodingKeys: String, CodingKey {
        case slug = "slug"
        case symbol = "symbol"
    }
}

'''

'''

success({
    data =     (
                {
            "circulating_supply" = 18697137;
            "cmc_rank" = 1;
            "date_added" = "2013-04-28T00:00:00.000Z";
            id = 1;
            "last_updated" = "2021-05-02T14:22:02.000Z";
            "max_supply" = 21000000;
            name = Bitcoin;
            "num_market_pairs" = 9549;
            platform = "<null>";
            quote =             {
                USD =                 {
                    "last_updated" = "2021-05-02T14:22:02.000Z";
                    "market_cap" = "1063000586851.752";
                    "percent_change_1h" = "0.09591311";
                    "percent_change_24h" = "-1.05109813";
                    "percent_change_30d" = "-4.45794679";
                    "percent_change_60d" = "11.80459387";
                    "percent_change_7d" = "14.06195861";
                    "percent_change_90d" = "69.54985569999999";
                    price = "56853.65555441735";
                    "volume_24h" = "40969975368.50657";
                };
            };
            slug = bitcoin;
            symbol = BTC;
            tags =             (
                mineable,
                pow,
                "sha-256",
                "store-of-value",
                "state-channels",
                "coinbase-ventures-portfolio",
                "three-arrows-capital-portfolio",
                "polychain-capital-portfolio",
                "binance-labs-portfolio",
                "arrington-xrp-capital",
                "blockchain-capital-portfolio",
                "boostvc-portfolio",
                "cms-holdings-portfolio",
                "dcg-portfolio",
                "dragonfly-capital-portfolio",
                "electric-capital-portfolio",
                "fabric-ventures-portfolio",
                "framework-ventures",
                "galaxy-digital-portfolio",
                "huobi-capital",
                "alameda-research-portfolio",
                "a16z-portfolio",
                "1confirmation-portfolio",
                "winklevoss-capital",
                "usv-portfolio",
                "placeholder-ventures-portfolio",
                "pantera-capital-portfolio",
                "multicoin-capital-portfolio",
                "paradigm-xzy-screener"
            );
            "total_supply" = 18697137;
        }, ...

'''


Solution

  • First, I think you might want to remove the API key in your example and reset it.

    Regarding your question. Your response starts with a data property. To parse this you would need start your struct there as well.

    So something like this should work;

    struct Coins: Decodable {
        let data: [Coin]
    
        struct Coin: Decodable {
            let symbol: String
            let slug: String
            let quote: [String: Quote]
        }
    
        struct Quote: Decodable {
            let price: Double
        }
    }
    

    I'm nesting the structs to preserve the namespace. Everything is pretty related. If you ever need access to one of them on it's own you could pull them out.

    Also you can omit the CodingKeys, since the key is the same as your variable name. Also I don't think you need optionals there, but I'm not completely familiar with the API.

    Furthermore I think you get only 1 set of data back and not an array of coins. So I would do the following here;

    let coins = try JSONDecoder().decode(Coins.self, from: data)