1

I need to decode a JSON formatted like this:

{
 "rates": {
"btc": {
  "name": "Bitcoin",
  "unit": "Ƀ",
  "value": 0,
  "type": "crypto"
},
"eth": {
  "name": "Ether",
  "unit": "Ξ",
  "value": 29.658,
  "type": "crypto"
},
"bch": {
  "name": "Bitcoin Cash",
  "unit": "BCH",
  "value": 12.3,
  "type": "crypto"
},
"ltc": {
  "name": "Litecoin",
  "unit": "Ł",
  "value": 108.219,
  "type": "crypto"
}...}

I currently get the data like this which I am not happy with:

import Foundation

class CGExchange {

static let shared = CGExchange()

var defaultCurrency = UserDefaults.standard.value(forKey: "DefaultCurrency")!

struct Rates: Codable {
    let rates: Currency
}

struct Exchange: Codable {

    let name: String
    let value: Double
    let unit: String

}

struct Currency: Codable {

    let btc: Exchange
    let eth: Exchange
    let bch: Exchange
    let ltc: Exchange
    let usd: Exchange
    let aed: Exchange
    let ars: Exchange
    let aud: Exchange
    let bdt: Exchange
    let bhd: Exchange
    let bmd: Exchange
    let brl: Exchange
    let cad: Exchange
    let chf: Exchange
    let clp: Exchange
    let cny: Exchange
    let czk: Exchange
    let dkk: Exchange
    let eur: Exchange
    let gbp: Exchange
    let hkd: Exchange
    let huf: Exchange
    let idr: Exchange
    let ils: Exchange
    let inr: Exchange
    let jpy: Exchange
    let krw: Exchange
    let kwd: Exchange
    let lkr: Exchange
    let mmk: Exchange
    let mxn: Exchange
    let myr: Exchange
    let nok: Exchange
    let nzd: Exchange
    let php: Exchange
    let pkr: Exchange
    let pln: Exchange
    let rub: Exchange
    let sar: Exchange
    let sek: Exchange
    let sgd: Exchange
    let thb: Exchange
    let `try`: Exchange
    let twd: Exchange
    let vef: Exchange
    let zar: Exchange
    let xdr: Exchange
    let xag: Exchange
    let xau: Exchange

}

var exchangeData = [Rates]()

func getData(arr: Bool, completion: @escaping (Bool) -> ()) {
    /// alamofire / urlsession request
    let urlJSON = "https://api.coingecko.com/api/v3/exchange_rates"

    guard let url = URL(string: urlJSON) else { return }

    URLSession.shared.dataTask(with: url) { (data, response, err) in

        guard let data = data else { return }

        do {

//                let currencies = try JSONDecoder().decode(Currency.self, from: data)
            let exchanges = try JSONDecoder().decode(Rates.self, from: data)
            self.exchangeData = [exchanges]

            print(self.exchangeData)
            completion(arr)

        } catch let jsonErr {
            print("error serializing json: \(jsonErr)")

        }

        }.resume()

}

func refresh(completion: () -> ()) {
    defaultCurrency = UserDefaults.standard.string(forKey: "DefaultCurrency")!
    completion()
}

}

The problem is I only need the values from 3 of the titles held in "rates" But that depends on a UserDefaults Value determined by the users choice. IE, If the user chooses GBP as their defaultCurrency, I need only to get let gbp from the struct currency, but it is awkward to get the value, as I can't change it. i.e. In my DataViewController to get the data I do let exchangeRate = CGExchange.shared.exchangeData[0].rates.gbp.value but ideally I need the struct to change based on what the default currency is. I.e. let exchangeRate = CGExchange.shared.exchangeData[0].rates.defaultCurrency.value

But I am struggling to find out how I would do that. If I could make a struct with values that could change such as:

struct Currency: Codable {
    let dc: Exchange
    let sc: Exchange
    let tc: Exchange

    enum CodingKeys: String, CodingKey {
        case dc = "\(defaultCurrency)"
        case sc = "\(secondCurrency)"
        case tc = "\(currency)"

    }

}

That is basically what I need but it says: 'Raw value for enum case must be a literal'

Any suggestions? Thank you.

Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
Peter Ruppert
  • 1,067
  • 9
  • 24

1 Answers1

1

You can try

struct Rates: Codable {
  let rates: [String:Exchange]
}

struct Exchange: Codable {

  let name: String
  let value: Double
  let unit: String 
}

Then use

let defCurrency = <#setHere#> // "btc"
let ex =  CGExchange.shared.exchangeData[0].rates[defCurrency]
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87