Reputation: 11987
I received excellent help in my previous question on how to set up my foundational JSON model. I am able to parse any values I want.
While I can parse any values I want, I am only able to access symbols or other values separately using dot notation.
btcSymbol = rawResponse.btc?.symbol
ethSymbol = rawResponse.eth?.symbol
I found other questions about iterating through dictionaries like Iterating Through a Dictionary in Swift but these examples are basic arrays and not multi-nested dictionaries using Swift's new protocols.
I want to be able to:
1. Iterate through the JSON and extract only the symbols from the CMC API.
2. Have a model where I am able to iterate all values of each currency separately so that I can later send those values to a table view for example.
BTC | name | symbol | marketCap | MaxSupply
ETH | name | symbol | marketCap | MaxSupply
Would restructuring my already existing model be the best solution? After my model is built would a standard for in loop or map be better?
JSONModel
struct RawServerResponse : Codable {
enum Keys : String, CodingKey {
case data = "data"
}
let data : [String:Base]
}
struct Base : Codable {
enum CodingKeys : String, CodingKey {
case id = "id"
case name = "name"
case symbol = "symbol"
}
let id : Int64
let name : String
let symbol : String
}
struct Quote : Codable {
enum CodingKeys : String, CodingKey {
case price = "price"
case marketCap = "market_cap"
}
let price : Double
let marketCap : Double
}
extension RawServerResponse {
enum BaseKeys : String {
case btc = "1"
case eth = "1027"
}
var btc : Base? { return data[BaseKeys.btc.rawValue] }
var eth : Base? { return data[BaseKeys.eth.rawValue] }
}
extension Base {
enum Currencies : String {
case usd = "USD"
}
var usd : Quote? { return quotes[Currencies.usd.rawValue]}
}
struct ServerResponse: Codable {
let btcName: String?
let btcSymbol: String?
init(from decoder: Decoder) throws {
let rawResponse = try RawServerResponse(from: decoder)
btcSymbol = rawResponse.btc?.symbol
JSON
{
"data": {
"1": {
"id": 1,
"name": "Bitcoin",
"symbol": "BTC",
"website_slug": "bitcoin",
"rank": 1,
"circulating_supply": 17041575.0,
"total_supply": 17041575.0,
"max_supply": 21000000.0,
"quotes": {
"USD": {
"price": 8214.7,
"volume_24h": 5473430000.0,
"market_cap": 139991426153.0,
"percent_change_1h": 0.09,
"percent_change_24h": 2.29,
"percent_change_7d": -2.44
}
}
}
Upvotes: 2
Views: 1907
Reputation: 285150
At least I'd recommend to map the data
dictionary to get the symbol
as key rather than the id
, by the way if the keys are camelCaseable and you pass the .convertFromSnakeCase
key decoding strategy you don't need any coding keys, for example
struct RawServerResponse : Codable {
var data = [String:Base]()
private enum CodingKeys: String, CodingKey { case data }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let baseDictionary = try container.decode([String:Base].self, forKey: .data)
baseDictionary.forEach { data[$0.1.symbol] = $0.1 }
}
}
struct Base : Codable {
let id : Int64
let name : String
let symbol : String
let quotes : [String:Quote]
}
struct Quote : Codable {
let price : Double
let marketCap : Double
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let rawResponse = try decoder.decode(RawServerResponse.self, from: data)
for (symbol, base) in rawResponse.data {
print(symbol, base.quotes["USD"]?.marketCap)
// ETH Optional(68660795252.0)
// BTC Optional(139991426153.0)
}
} catch { print(error) }
Upvotes: 3