Okay, this is getting frustrating... Can you help me?

Hey y’all, I am getting an error when parsing some data from an API, and when I try to parse it, I get this error:

typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "price_date", intValue: nil)], debugDescription: "Expected to decode Double but found a string/data instead.", underlyingError: nil))

My code:

import SwiftUI

// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
//   let welcome = try? newJSONDecoder().decode(Welcome.self, from: jsonData)



// MARK: - Welcome
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
//   let welcome = try? newJSONDecoder().decode(Welcome.self, from: jsonData)


// MARK: - CryptoData
struct CryptoData: Codable {
    let id, currency, symbol, name: String
    let logoURL: String
    let status: Status
    let price: String
    let priceDate, priceTimestamp: Date
    let circulatingSupply: String
    let maxSupply: String?
    let marketCap, marketCapDominance, numExchanges, numPairs: String
    let numPairsUnmapped: String
    let firstCandle: Date
    let firstTrade, firstOrderBook: Date?
    let rank, rankDelta, high: String
    let highTimestamp: Date
    let the1D, the30D: The1_D
    let firstPricedAt: String?

    enum CodingKeys: String, CodingKey {
        case id, currency, symbol, name
        case logoURL = "logo_url"
        case status, price
        case priceDate = "price_date"
        case priceTimestamp = "price_timestamp"
        case circulatingSupply = "circulating_supply"
        case maxSupply = "max_supply"
        case marketCap = "market_cap"
        case marketCapDominance = "market_cap_dominance"
        case numExchanges = "num_exchanges"
        case numPairs = "num_pairs"
        case numPairsUnmapped = "num_pairs_unmapped"
        case firstCandle = "first_candle"
        case firstTrade = "first_trade"
        case firstOrderBook = "first_order_book"
        case rank
        case rankDelta = "rank_delta"
        case high
        case highTimestamp = "high_timestamp"
        case the1D = "1d"
        case the30D = "30d"
        case firstPricedAt = "first_priced_at"
    }
}

enum Status: String, Codable {
    case active = "active"
}

// MARK: - The1_D
struct The1_D: Codable {
    let volume, priceChange, priceChangePct, volumeChange: String
    let volumeChangePct: String
    let marketCapChange, marketCapChangePct: String?

    enum CodingKeys: String, CodingKey {
        case volume
        case priceChange = "price_change"
        case priceChangePct = "price_change_pct"
        case volumeChange = "volume_change"
        case volumeChangePct = "volume_change_pct"
        case marketCapChange = "market_cap_change"
        case marketCapChangePct = "market_cap_change_pct"
    }
}

typealias Crypto = [CryptoData]


class Api {
    func getPosts() {
        guard let url = URL(string: "https://api.nomics.com/v1/currencies/ticker?key=3fad5462303f05f24f5d679f2e2f16e1339b446d&interval=1d,30d&convert=USD&per-page=1000") else { return }
        
        URLSession.shared.dataTask(with: url) { (data, _, _) in
            do {
                let coins = try JSONDecoder().decode([CryptoData].self, from: data!)
                print(coins)
            } catch {
                print(error)
                //add some better error handling here
            }
           
        }
        .resume()
    }
}

My view:

import SwiftUI

struct CryptoView: View {
    var body: some View {
        Text("Hello, World!")
            .onAppear {
                Api().getPosts()
            }
    }
}

struct CryptoView_Previews: PreviewProvider {
    static var previews: some View {
        CryptoView()
    }
}

Please help!

What does your JSON look like? The error is you have a double for price_date in your structure, but it’s finding a string

Change the data type of price_date

The issue is that Date is by default stored in JSON as a Double but what you have in this API result is an ISO8601 date string.

So when you declare

let priceDate, priceTimestamp: Date

JSONDecoder looks for a Double but finds a String and chokes.

If you do this:

            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            do {
                let coins = try decoder.decode([CryptoData].self, from: data!)
                print(coins)
            } catch {
                print(error)
                //add some better error handling here
            }

then it should work.

@mikaelacaron
@roosterboy
Thank you guys, the API call got parsed, but I am getting an error when I try to implement it into the SwiftUI view.
My error:

Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project

My struct:

import SwiftUI

struct CryptoView: View {
    @State var coins: [CryptoData] = []
    var body: some View {
        List(coins) { coin in
            Text(coin.symbol)
        }
            .onAppear {
                Api().getPosts { (coins) in
                    self.coins = coins
                }
            }
    }
}

struct CryptoView_Previews: PreviewProvider {
    static var previews: some View {
        CryptoView()
    }
}

My model stayed the same, except where @roosterboy helped with the decoding…

You need to conform CryptoData to Identifiable. Since there’s already an id property, this is easy to do; simple change the type declaration to struct CryptoData: Codable, Identifiable {

List and ForEach require that 1) the items they loop over conform to Identifiable, or 2) you supply an explicit id parameter in the call (e.g., something like List(items, id: \.itemID) or similar).

Also, I’m assuming you’ve modified your getPosts() method to take a completion handler since the way you are calling it in onAppear has now changed. If not, you’ll get a new and different error.

Finally, the weird error message you got is an artifact of Xcode 12’s sometimes loose relationship with the truth when it comes to reporting errors. If you were to try this same code in Xcode 13 beta, you’d get a better error message that would point to the actual problem.

1 Like

@roosterboy
Woah, you guys are lifesavers! It worked and I am so happy about this!

1 Like