通过相同的结构从不同的API获取数据?

时间:2018-01-11 19:06:59

标签: swift api struct

是否可以通过相同的结构从不同但相似的API(不同的URL但类似的JSON构造)获取数据?

例如,使用bitcoinUSD.raw.eth.usd.pricebitcoinUSD.raw.eth.gbp.price访问数据:

struct Bitcoin : Decodable {
    private enum CodingKeys : String, CodingKey { case raw = "RAW" }
    let raw : BitcoinRAW
}

struct BitcoinRAW : Decodable {
    private enum CodingKeys : String, CodingKey { case btc = "BTC" }
    let btc : BitcoinETH
}

struct BitcoinETH : Decodable {
    private enum CodingKeys : String, CodingKey {
        case usd = "USD"
        case gbp = "GBP"
    }
    let usd : BitcoinUSD
    let gbp : BitcoinGBP
}

struct BitcoinUSD : Decodable {
    let price : Double
    let percentChange24h : Double

    private enum CodingKeys : String, CodingKey {
        case price = "PRICE"
        case percentChange24h = "CHANGEPCT24HOUR"
    }
}

struct BitcoinGBP : Decodable {
    let price : Double
    let percentChange24h : Double

    private enum CodingKeys : String, CodingKey {
        case price = "PRICE"
        case percentChange24h = "CHANGEPCT24HOUR"
    }
}

以下是我的数据提取功能:

enum MyErrorBTC : Error {
    case FoundNil(String)
}

class BitcoinInfo : NSObject {

    static var btcURL : URL?

    func fetchBitcoinInfo(completion: @escaping (Bitcoin?, Error?) -> Void) {

        if WalletViewController.currencyUSD == true {
            BitcoinInfo.btcURL = URL(string: "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC&tsyms=USD")!
            let task = URLSession.shared.dataTask(with: BitcoinInfo.btcURL!) { (data, response, error) in
                guard let data = data else { return }
                do {
                    if let bitcoinPrice = try? JSONDecoder().decode(Bitcoin.self, from: data) {
                        completion(bitcoinPrice, nil)
                        //print("price =", bitcoinUSD.raw.btc.usd.price)
                        throw MyErrorBTC.FoundNil("bitcoinPrice")
                    }
                } catch {
                    print(error)
                }
            }
            task.resume()
        } else if WalletViewController.currencyGBP == true {
            BitcoinInfo.btcURL = URL(string: "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC&tsyms=GBP")!
            let task = URLSession.shared.dataTask(with: BitcoinInfo.btcURL!) { (data, response, error) in
                guard let data = data else { return }
                do {
                    if let bitcoinPrice = try? JSONDecoder().decode(Bitcoin.self, from: data) {
                        completion(bitcoinPrice, nil)
                        //print("price =", bitcoinUSD.raw.btc.gbp.price)
                        throw MyErrorBTC.FoundNil("bitcoinPrice")
                    }
                } catch {
                    print(error)
                }
            }
            task.resume()
        }
    }
}

请注意,当调用任一函数时,此代码返回nil。 有没有办法修改它?

2 个答案:

答案 0 :(得分:0)

如果您的目标只是让您的获取功能更具可重用性,那么两个端点之间的唯一区别就是货币。在这种情况下,为什么不直接将货币作为参数传递,并使用您获得的货币参数构建您的URL。这看起来像这样:

func fetchBitcoinInfo(forCurrency currency: String, _ completion: @escaping (Bitcoin?, Error?) -> Void) {
    BitcoinInfo.btcURL = URL(string: "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=BTC&tsyms=\(currency)")!
    let task = URLSession.shared.dataTask(with: BitcoinInfo.ethURL!) { (data, response, error) in
        guard let data = data else { return }
        do {
            if let bitcoinPrice = try? JSONDecoder().decode(Bitcoin.self, from: data) {
                completion(bitcoinPrice, nil)
            } else {
                throw MyErrorBTC.FoundNil("bitcoinPrice")
            }
        } catch {
            print(error)
        }
    }
    task.resume()
}

顺便说一下,当你 获得有效的FoundNil对象时,你正在抛出Bitcoin,这没有任何意义。我将您的错误抛出到else语句中,这样只有在bitcoinPrice展开失败时它才会抛出。

答案 1 :(得分:0)

是的,但您应该将变量声明为可选:

struct BitcoinETH : Decodable {
    private enum CodingKeys : String, CodingKey {
        case usd = "USD"
        case gbp = "GBP"
    }
    let usd : BitcoinUSD?
    let gbp : BitcoinGBP?
}

如果没有,将抛出异常并且您将获得整个结构的零值,因为它不会被解码为解码器预期的