when i try to make decoder.decode i get result nil (i check the data and there is data, data != nil) but the decode not working I think most likely it is related to the structs, I would love to refine what I am doing wrong. i added the code below Thanks for the help
struct codable :
struct CoinsList: Codable {
let data:[Coin]?
}
struct Coin:Codable {
let id:Int?
let name:String?
let symbol:String?
let max_supply:Int64?
let circulating_supply:Int64?
let total_supply: Int64?
let cmc_rank:Int?
let quote:QuoteData?
}
struct QuoteData:Codable {
let USD:CoinPrice?
}
struct CoinPrice:Codable {
let price:Double?
let volume_24h:Double?
let market_cap:Double?
}
json:
{
status: {
timestamp: "2022-10-04T12:34:53.878Z",
error_code: 0,
error_message: null,
elapsed: 20,
credit_count: 1,
notice: null,
total_count: 9462
},
data: [
{
id: 1,
name: "Bitcoin",
symbol: "BTC",
slug: "bitcoin",
num_market_pairs: 9760,
date_added: "2013-04-28T00:00:00.000Z",
tags: [
"mineable",
"pow",
"sha-256",
"store-of-value",
"state-channel",
"coinbase-ventures-portfolio",
"three-arrows-capital-portfolio",
"polychain-capital-portfolio",
"binance-labs-portfolio",
"blockchain-capital-portfolio",
"boostvc-portfolio",
"cms-holdings-portfolio",
"dcg-portfolio",
"dragonfly-capital-portfolio",
"electric-capital-portfolio",
"fabric-ventures-portfolio",
"framework-ventures-portfolio",
"galaxy-digital-portfolio",
"huobi-capital-portfolio",
"alameda-research-portfolio",
"a16z-portfolio",
"1confirmation-portfolio",
"winklevoss-capital-portfolio",
"usv-portfolio",
"placeholder-ventures-portfolio",
"pantera-capital-portfolio",
"multicoin-capital-portfolio",
"paradigm-portfolio"
],
max_supply: 21000000,
circulating_supply: 19168887,
total_supply: 19168887,
platform: null,
cmc_rank: 1,
self_reported_circulating_supply: null,
self_reported_market_cap: null,
tvl_ratio: null,
last_updated: "2022-10-04T12:32:00.000Z",
quote: {
USD: {
price: 19944.28244328565,
volume_24h: 33376911598.804485,
volume_change_24h: 35.5843,
percent_change_1h: 0.10558095,
percent_change_24h: 3.77987746,
percent_change_7d: -1.62664338,
percent_change_30d: 0.4638364,
percent_change_60d: -14.4615399,
percent_change_90d: -0.70303523,
market_cap: 382309696451.4265,
market_cap_dominance: 39.9685,
fully_diluted_market_cap: 418829931309,
tvl: null,
last_updated: "2022-10-04T12:32:00.000Z"
}
}
},
}
reuqset api :
class CoinRestApi {
private let urlCoinList = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest"
private var coinDataSource:((CoinsList?)->Void)?
private var coins: CoinsList?
func getCoinsLists(coinDetaClousre: @escaping (_ coinDataSource:CoinsList?)->Void) {
self.coinDataSource = coinDetaClousre
let url = URL(string: self.urlCoinList)
guard url != nil else {
return
}
let session = URLSession.shared
let dataTask = session.dataTask(with: url!, completionHandler: ParseCoin)
dataTask.resume()
}
private func ParseCoin(data:Data? , urlResponse:URLResponse? , error:Error?) {
if error == nil && data != nil {
// parse JSON
let decoder = JSONDecoder()
do {
self.coins = try decoder.decode(CoinsList.self, from: data!)
}
catch {
print("Parseing Failed")
}
}
if let coinDataSource {
coinDataSource(coins)
}
}
CodePudding user response:
I tried your code, and it worked just fine. The JSON data you shared has a wrong format and as @workingdog said you get API key missing when trying to call the URL. So I created a data.json file with the structure fixed and tested the decode method locally and it worked ok. I suggest you use the code below to check the error you are getting, but the problem is not in the Codable structs.
let decoder = JSONDecoder()
do {
self.coins = try decoder.decode(CoinsList.self, from: data!)
}
catch(let error){
print("Parsing Failed \(error)")
}
CodePudding user response:
The errors you get is probably due to your data model structs not matching the json data from the server. Try this example code. Since I do not have an api key, I've used the "test" key with the "sandbox" api, which should be the same as the "pro-api".
class CoinRestApi: ObservableObject {
@Published var coinsList: [Coin] = []
func fetchData(token: String) {
// var urlComponents = URLComponents(string: "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest")
// for testing, sandbox-api
var urlComponents = URLComponents(string: "https://sandbox-api.coinmarketcap.com/v1/cryptocurrency/listings/latest")
guard let url = urlComponents?.url else {
print("Invalid url")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("\(token)", forHTTPHeaderField: "X-CMC_PRO_API_KEY")
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
DispatchQueue.main.async {
do {
let results = try JSONDecoder().decode(CryptoResponse.self, from: data)
self.coinsList = results.data
} catch {
print(error) // <-- here important
}
}
}.resume()
}
}
struct ContentView: View {
@StateObject var model = CoinRestApi()
var body: some View {
VStack {
List(model.coinsList) { coin in
VStack {
Text(coin.symbol)
Text("\(coin.quote["USD"]?.price ?? 0.0)")
}
}
}
.onAppear {
model.fetchData(token: "b54bcf4d-1bca-4e8e-9a24-22ff2c3d462c")
}
}
}
// MARK: - CryptoResponse
struct CryptoResponse: Codable {
let status: Status
let data: [Coin]
}
// MARK: - Coin
struct Coin: Identifiable, Codable {
let id: Int
let name, symbol, slug: String
let cmcRank, numMarketPairs, circulatingSupply, totalSupply: Int
let maxSupply: Int
let lastUpdated, dateAdded: String
let tags: [String]
let platform, selfReportedCirculatingSupply, selfReportedMarketCap: String?
let quote: [String: Quote]
enum CodingKeys: String, CodingKey {
case id, name, symbol, slug
case cmcRank = "cmc_rank"
case numMarketPairs = "num_market_pairs"
case circulatingSupply = "circulating_supply"
case totalSupply = "total_supply"
case maxSupply = "max_supply"
case lastUpdated = "last_updated"
case dateAdded = "date_added"
case tags, platform
case selfReportedCirculatingSupply = "self_reported_circulating_supply"
case selfReportedMarketCap = "self_reported_market_cap"
case quote
}
}
// MARK: - Quote
struct Quote: Codable {
let price: Double
let volume24H: Int
let volumeChange24H, percentChange1H, percentChange24H, percentChange7D: Double?
let marketCap: Double
let marketCapDominance: Int
let fullyDilutedMarketCap: Double
let lastUpdated: String
enum CodingKeys: String, CodingKey {
case price
case volume24H = "volume_24h"
case volumeChange24H = "volume_change_24h"
case percentChange1H = "percent_change_1h"
case percentChange24H = "percent_change_24h"
case percentChange7D = "percent_change_7d"
case marketCap = "market_cap"
case marketCapDominance = "market_cap_dominance"
case fullyDilutedMarketCap = "fully_diluted_market_cap"
case lastUpdated = "last_updated"
}
}
// MARK: - Status
struct Status: Codable {
let timestamp: String
let errorCode: Int
let errorMessage: String?
let elapsed, creditCount: Int
let notice: String?
enum CodingKeys: String, CodingKey {
case timestamp
case errorCode = "error_code"
case errorMessage = "error_message"
case elapsed
case creditCount = "credit_count"
case notice
}
}