Can't decode JSON data from API

后端 未结 1 2046
时光取名叫无心
时光取名叫无心 2021-01-07 11:49

I am trying to update labels with data fetched from an API to print Bitcoin price and percentage variation in an app but I can\'t figure out how to properly decode the JSON.

相关标签:
1条回答
  • 2021-01-07 12:11

    The main problem is the price and percentage are Strings not Doubles. Btw it returns an array so you need to use [Bitcoin].self type when decoding it:

    This is how your codable struct should look like:

    struct Bitcoin: Codable {
        let id: String
        let name: String
        let symbol: String
        let rank: String
        let priceUSD: String
        let priceBTC: String
        let volume24hUSD: String
        let marketCapUSD: String
        let availableSupply: String
        let totalSupply: String
        let maxSupply: String
        let percentChange1h: String
        let percentChange24h: String
        let percentChange7d: String
        let lastUpdated: String
        let priceEUR: String
        let volume24hEUR: String
        let marketCapEUR: String
        private enum CodingKeys: String, CodingKey {
            case id, name, symbol, rank,
            priceUSD = "price_usd",
            priceBTC = "price_btc",
            volume24hUSD = "24h_volume_usd",
            marketCapUSD = "market_cap_usd",
            availableSupply = "available_supply",
            totalSupply = "total_supply",
            maxSupply = "max_supply",
            percentChange1h = "percent_change_1h",
            percentChange24h = "percent_change_24h",
            percentChange7d = "percent_change_7d",
            lastUpdated = "last_updated",
            priceEUR = "price_eur",
            volume24hEUR = "24h_volume_eur",
            marketCapEUR = "market_cap_eur"
        }
    }
    

    And this is how you should decode the json array returned by the API and get its first element:

    do {
        if let bitcoinEUR = try JSONDecoder().decode([Bitcoin].self, from: data).first {
            print(bitcoinEUR)
            print(bitcoinEUR.priceEUR)
            print(bitcoinEUR.percentChange1h)
        }
    } catch {
        print(error)
    }
    

    If you are only interested in those two properties you can set your bitcoin structure like this:

    struct Bitcoin: Codable {
        let percentChange1h: String
        let priceEUR: String
        private enum CodingKeys: String, CodingKey {
            case percentChange1h = "percent_change_1h", priceEUR = "price_eur"
        }
    }
    

    edit/update:

    Note: You are displaying euro price using the dollar currency symbol. If you need to format your euro price with 2 fraction digits you will need to initialize first a new Floating point object with the string returned by the API.

    So you can extend the Bitcoin API with two computed properties, one to convert the euro price string to Decimal and the other to format the decimal value into currency:

    extension Bitcoin {
        var priceEURdecimal: Decimal {
            return Decimal(string: priceEUR) ?? 0
        }
        var priceEURcurrency: String {
            Formatter.currency.locale = Locale(identifier: "fr_FR")
            return Formatter.currency.string(for: priceEURdecimal) ?? ""
        }
    }
    

    You will need also to add those extensions to a new Swift file in your project to help you format the currency:

    extension NumberFormatter {
        convenience init(numberStyle: Style) {
            self.init()
            self.numberStyle = numberStyle
        }
    }
    extension Formatter {
        static let currency = NumberFormatter(numberStyle: .currency)
    }
    

    Usage:

    do {
        if let bitcoinEUR = try JSONDecoder().decode([Bitcoin].self, from: data).first {
            print(bitcoinEUR.priceEURdecimal)   // "13823.952495\n"
            print(bitcoinEUR.priceEURcurrency)  // "13 823,95 €\
        }
    } catch {
        print(error)
    }
    
    0 讨论(0)
提交回复
热议问题