The error is popping up, but it doesn't make much sense. Can you help me figure it out?

Hey everyone! I am trying to parse a JSON file from a crypto API. This is my error:

Thread 4: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary 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)

import Foundation

// MARK: - Welcome
struct CryptoData: Codable {
    let status: String
    let data: DataClass
}

// MARK: - DataClass
struct DataClass: Codable {
    let stats: Stats
    let base: Base
    let coins: [Coin]
}

// MARK: - Base
struct Base: Codable {
    let symbol, sign: String
}

// MARK: - Coin
struct Coin: Codable {
    let id: Int
    let uuid, slug, symbol, name: String
    let coinDescription, color: String?
    let iconType: IconType
    let iconURL: String
    let websiteURL: String?
    let socials, links: [Link]
    let confirmedSupply: Bool
    let numberOfMarkets, numberOfExchanges: Int
    let type: CoinType
    let volume, marketCap: Int
    let price: String
    let circulatingSupply, totalSupply: Double?
    let approvedSupply: Bool
    let firstSeen, listedAt: Int
    let change: Double
    let rank: Int
    let history: [String]
    let allTimeHigh: AllTimeHigh
    let penalty: Bool

    enum CodingKeys: String, CodingKey {
        case id, uuid, slug, symbol, name
        case coinDescription = "description"
        case color, iconType
        case iconURL = "iconUrl"
        case websiteURL = "websiteUrl"
        case socials, links, confirmedSupply, numberOfMarkets, numberOfExchanges, type, volume, marketCap, price, circulatingSupply, totalSupply, approvedSupply, firstSeen, listedAt, change, rank, history, allTimeHigh, penalty
    }
}

// MARK: - AllTimeHigh
struct AllTimeHigh: Codable {
    let price: String
    let timestamp: Int
}

enum IconType: String, Codable {
    case pixel = "pixel"
    case vector = "vector"
}

// MARK: - Link
struct Link: Codable {
    let name: String
    let type: LinkType
    let url: String
}

enum LinkType: String, Codable {
    case bitcointalk = "bitcointalk"
    case discord = "discord"
    case explorer = "explorer"
    case facebook = "facebook"
    case github = "github"
    case instagram = "instagram"
    case linkedin = "linkedin"
    case medium = "medium"
    case reddit = "reddit"
    case sinaWeibo = "sina-weibo"
    case telegram = "telegram"
    case twitter = "twitter"
    case vkontakte = "vkontakte"
    case website = "website"
    case wechat = "wechat"
    case youtube = "youtube"
}

enum CoinType: String, Codable {
    case coin = "coin"
}

// MARK: - Stats
struct Stats: Codable {
    let total, offset, limit: Int
    let order, base: String
    let totalMarkets, totalExchanges: Int
    let totalMarketCap, total24HVolume: Double

    enum CodingKeys: String, CodingKey {
        case total, offset, limit, order, base, totalMarkets, totalExchanges, totalMarketCap
        case total24HVolume = "total24hVolume"
    }
}

class Api {
    func getPosts() {
        guard let url = URL(string: "https://api.coinranking.com/v1/public/coins") else { return }
        
        URLSession.shared.dataTask(with: url) { (data, _, _) in
            let coins = try! JSONDecoder().decode(CryptoData.self, from: data!)
            print(coins)
        }
        .resume()
    }
}

My view:

import SwiftUI

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

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

Help me please! Thank you!

Please post the error you’re seeing, also titling your forum post with the error helps others find the post if they have the same issue rather than just “error popping up”

@mikaelacaron
I have posted it now!

When you use try! you are telling the compiler “Try this and if it fails, I don’t care if you crash”. For whatever reason, your decoding is failing and so you crashed. try! is almost never a good idea.

Your decoding code works, so you just need to add better error handling for those cases when something fails. You can start with something like this:

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

and build on it from there.

@roosterboy
I’m sorry, I forgot to use the original code. I had tried out other stuff so the code had a little mistake in it… Please see the modified code above. I put

[ ]

around the CryptoData.self, and with this code, I get an error. Without the code, I just get a blank space in my console.

The original code you posted works fine if you make the change I suggested (i.e., don’t use try! and use a do {} catch {} block).

@roosterboy
With the solution you provided, I got 2 new errors:

decodingError(err: "The data couldn’t be read because it is missing.")
keyNotFound(CodingKeys(stringValue: "Global Quote", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"Global Quote\", intValue: nil) (\"Global Quote\").", underlyingError: nil))

What should I do here? My code is still the same…:frowning:

Using the code you posted, including the API URL, I get no result with a key of “Global Quote”. Are you sure you’re using the exact same code?

If you post the API URL into a browser window, do you see a key called “Global Quote”?

And on the other side, I see no key in your code with a value of “Global Quote”. So where is this coming from?

@roosterboy
Hmm… I don’t see one in the API JSON too…