I have read How to decode a nested JSON struct with Swift Decodable protocol? It does not address my specific use case where string literal number values are used as root dictionaries.
Also How to decode a nested JSON struct with Swift Decodable protocol? answer from Imanou Petit. Can't decode JSON data from API answer from Leo Dabus.
The currencies are dictionaries themselves represented by literal string numbers inside of the data
dictionary so this is throwing me off. I am looking for the most Swifty 4 model using enums where it is easy to see what containers correspond to what dictionaries.
p.s. David Berry gave a great answer below which I have implemented. If anyone else has other approaches to get to the same result I'd love to see different suggestions. Maybe there are some newer Swift 4 methods that are not well known yet or other design patterns.
Code
struct RawServerResponse {
enum RootKeys: String, CodingKey {
case data
case btc = "1"
case eth = "1027"
case iota = "1720"
case ripple = "52"
case neo = "1376"
case quotes
case USD
}
enum BaseKeys: String, CodingKey {
case id, name, symbol, maxSupply = "max_supply"
}
enum QuotesKeys: String, CodingKey {
case USD
}
enum USDKeys: String, CodingKey {
case price, marketCap = "market_cap"
}
let data: String
let id: Int
let name: String
let symbol: String
let maxSupply: Double
let price: Double
let marketCap: Double
}
extension RawServerResponse: Decodable {
init(from decoder: Decoder) throws {
// data
let container = try decoder.container(keyedBy: RootKeys.self)
data = try container.decode(String.self, forKey: .data)
// id
let idContainer = try container.nestedContainer(keyedBy: BaseKeys.self, forKey: .data)
id = try idContainer.decode(Int.self, forKey: .id)
// price
let priceContainer = try container.nestedContainer(keyedBy: USDKeys.self, forKey: .USD)
price = try priceContainer.decode(Double.self, forKey: .price)
}
}
API / JSON https://api.coinmarketcap.com/v2/ticker/
{
"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
}
},
"last_updated": 1526699671
},
"1027": {
"id": 1027,
"name": "Ethereum",
"symbol": "ETH",
"website_slug": "ethereum",
"rank": 2,
"circulating_supply": 99524121.0,
"total_supply": 99524121.0,
"max_supply": null,
"quotes": {
"USD": {
"price": 689.891,
"volume_24h": 2166100000.0,
"market_cap": 68660795252.0,
"percent_change_1h": 0.13,
"percent_change_24h": 2.51,
"percent_change_7d": 2.54
}
},
"last_updated": 1526699662
}
}