我从API中获取数据:
enum MyError : Error {
case FoundNil(String)
}
struct Crypto : Decodable {
private enum CodingKeys : String, CodingKey { case raw = "RAW" }
let raw : CryptoRAW
}
struct CryptoRAW : Decodable {
private enum CodingKeys : String, CodingKey {
case btc = "BTC"
case xrp = "XRP"
}
let btc : CryptoCURRENCIES?
let xrp : CryptoCURRENCIES?
}
struct CryptoCURRENCIES : Decodable {
private enum CodingKeys : String, CodingKey {
case usd = "USD"
case eur = "EUR"
}
let usd : CryptoCURRENCY?
let eur : CryptoCURRENCY?
}
struct CryptoCURRENCY : Decodable {
let price : Double
let percentChange24h : Double
private enum CodingKeys : String, CodingKey {
case price = "PRICE"
case percentChange24h = "CHANGEPCT24HOUR"
}
}
class CryptoInfo : NSObject {
enum FetchError: Error {
case urlError
case unknownNetworkError
}
func fetchCryptoInfo(forCrypto crypto: String, forCurrency currency: String, _ completion: @escaping (Crypto?, Error?) -> Void) {
let url = URL(string: "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=\(crypto)&tsyms=\(currency)")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data, error == nil else {
completion(nil, error ?? FetchError.unknownNetworkError)
return
}
do {
let crypto = try JSONDecoder().decode(Crypto.self, from: data); completion(crypto, nil)
} catch let parseError {
completion(nil, parseError)
}
}
task.resume()
}
}
正如您所看到的,我可以通过相同的结构获得不同的内容:btc
或xrp
中的usd
或eur
,具体取决于调用函数中传递的参数(不包括试图保持代码尽可能短)。
当我想访问API返回的Double
值时,我就是这样做的:
if let price = Crypto.raw.btc?.usd?.price { MainViewController.bitcoinDoublePrice = price }
一切都很好但我在这里有一个很大的优化问题:我需要做的是在btc
和xrp
中同时获得usd
和eur
:< / p>
if let price = Crypto.raw.btc?.usd?.price { MainViewController.bitcoinUSDDoublePrice = price }
if let price = Crypto.raw.btc?.eur?.price { MainViewController.bitcoinEURDoublePrice = price }
if let price = Crypto.raw.xrp?.usd?.price { MainViewController.rippleUSDDoublePrice = price }
if let price = Crypto.raw.xrp?.eur?.price { MainViewController.rippleEURDoublePrice = price }
如果我只有这四个,那就没关系,但是我需要以5种不同的货币获取25种不同的密码+它们在不同时间范围内的百分比变化。
我猜你现在明白了这一点。
我可以通过向函数传递参数来动态替换btc
中的let price = cryptoInfo.raw.btc?.usd?.price
或eur
中的let price = cryptoInfo.raw.xrp?.eur?.price
,或者可能有不同的方法来避免重复我无法想到?
示例JSON输入:
"RAW":{
"BTC":{
"USD":{
"TYPE":"5",
"MARKET":"CCCAGG",
"FROMSYMBOL":"BTC",
"TOSYMBOL":"USD",
"FLAGS":"4",
"PRICE":10248.64,
"LASTUPDATE":1519669598,
"LASTVOLUME":0.14558,
"LASTVOLUMETO":1489.13782,
"LASTTRADEID":"203305344",
"VOLUMEDAY":92548.48622803023,
"VOLUMEDAYTO":924032126.7547476,
"VOLUME24HOUR":107957.56694427232,
"VOLUME24HOURTO":1072399848.5990984,
"OPENDAY":9610.11,
"HIGHDAY":10409.28,
"LOWDAY":9411.82,
"OPEN24HOUR":9466.87,
"HIGH24HOUR":10414.1,
"LOW24HOUR":9396.22,
"LASTMARKET":"Bitfinex",
"CHANGE24HOUR":781.7699999999986,
"CHANGEPCT24HOUR":8.257956431217483,
"CHANGEDAY":638.5299999999988,
"CHANGEPCTDAY":6.644356828381764,
"SUPPLY":16881800,
"MKTCAP":173015490752,
"TOTALVOLUME24H":470883.0751374748,
"TOTALVOLUME24HTO":4791892728.888281
}
}
},
"DISPLAY":{
"BTC":{
"USD":{
"FROMSYMBOL":"Ƀ",
"TOSYMBOL":"$",
"MARKET":"CryptoCompare Index",
"PRICE":"$ 10,248.6",
"LASTUPDATE":"Just now",
"LASTVOLUME":"Ƀ 0.1456",
"LASTVOLUMETO":"$ 1,489.14",
"LASTTRADEID":"203305344",
"VOLUMEDAY":"Ƀ 92,548.5",
"VOLUMEDAYTO":"$ 924,032,126.8",
"VOLUME24HOUR":"Ƀ 107,957.6",
"VOLUME24HOURTO":"$ 1,072,399,848.6",
"OPENDAY":"$ 9,610.11",
"HIGHDAY":"$ 10,409.3",
"LOWDAY":"$ 9,411.82",
"OPEN24HOUR":"$ 9,466.87",
"HIGH24HOUR":"$ 10,414.1",
"LOW24HOUR":"$ 9,396.22",
"LASTMARKET":"Bitfinex",
"CHANGE24HOUR":"$ 781.77",
"CHANGEPCT24HOUR":"8.26",
"CHANGEDAY":"$ 638.53",
"CHANGEPCTDAY":"6.64",
"SUPPLY":"Ƀ 16,881,800.0",
"MKTCAP":"$ 173.02 B",
"TOTALVOLUME24H":"Ƀ 470.88 K",
"TOTALVOLUME24HTO":"$ 4,791.89 M"
}
}
}
}
谢谢!
答案 0 :(得分:1)
我认为你需要切换到较低级别的JSON API,因为设计糟糕的API有许多可能的密钥名称:每种货币类型一个。真的,你只需要第一个最里面的结构,然后剥掉外壳。
这个JSON非常糟糕,数字类型埋藏在具有奇异空格的字符串类型中。有人不明白。 “$ 4,791.89 M”??? 在地球上,我们称之为:4791890000 [编辑:实际上这是JSON的DISPLAY部分,但这只是一种可怕的不同方式。完全浪费。]
如果您是新手,这是一个令人困惑的学习示例。
请参阅:https://grokswift.com/json-swift-4/
低级API的本质是它不立即用JSON数据创建一个好的Swift对象,而是一个键值对的字典,你必须小心翼翼地验证您想要的每个键是否存在,并且每个值都是您想要的类型。但是,您可以获得完全的灵活性。
将数据提取到词典中:
if let outerJSON = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: Any], ...
现在你可以这样说:
// crypto, currency are the input Strings in your code above
if let cryptoJSON: [String: Any] = outerJSON["RAW"] {
if let currencyJSON: [String: Any] = cryptoJSON[crypto] {
if let actualJSON: [String: Any] = currencyJSON[currency] {
let myActualData = TradingData(actualJSON) // Correct O-O
// or to show low level example:
if let price = actualJSON["PRICE"] as? Double {
// Avoid the DISPLAY portion of the JSON and you're OK
// Also the above code implies a problem with the View.
// You probably don't want to maintain an output for every
// combination of crypto and fiat currency.
// Unless you're using a visual data structure that *grows*
// like a UITable. Perhaps:
MainViewController.currencyLabel.text = currency
MainViewController.cryptoLabel.text = crypto
MainViewController.conversionRateLabel.text = "\(price)"
}
}
}
}