简体   繁体   English

使用多个独立的 API 调用创建 object

[英]Create object using multiple independent API calls

I am working on a practice project with the CoinGecko API.我正在使用 CoinGecko API 进行练习项目。 I just figured out how to successfully fetch, parse, and display data from the API, but I have ran into an issue.我刚刚弄清楚如何从 API 成功获取、解析和显示数据,但我遇到了一个问题。 Currently I am only fetching a single coin, using a single URL but I would like to be able to fetch multiple (3 in this case), all at once and display them.目前我只使用单个 URL 获取单个硬币,但我希望能够一次获取多个(在本例中为 3 个)并显示它们。 Not sure how to proceed, or if my explanation makes sense so I have attached my code below.不知道如何继续,或者我的解释是否有意义,所以我在下面附上了我的代码。 Thanks in advance.提前致谢。

CoinListViewModel CoinListViewModel

import Foundation

class CoinListViewModel {
    private(set) var coin: Coin
    
    init(coin: Coin) {
        self.coin = coin
    }
    
    func getCoins(url: URL) async {
        do {
            let coin = try await WebService().getCoins(url: url)
            
            self.coin = coin
            
        } catch {
            print(error)
        }
    }
}

struct CoinViewModel {
    private let coin: Coin
    
    init(coin: Coin) {
        self.coin = coin
    }
    
    var symbol: String {
        coin.symbol
    }
    
    var name: String {
        coin.name 
    }
    
    var price: [String: Double] {
        coin.marketData.currentPrice
    }
}

WebService网络服务

import Foundation

enum CoinsError: Error {
    case invalidServerResponse
}

class WebService {
    
    func getCoins(url: URL) async throws -> Coin {
        
        let (data, response) = try await URLSession.shared.data(from: url)
        
        guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                throw CoinsError.invalidServerResponse
            }
        
        return try JSONDecoder().decode(Coin.self, from: data)
    }
}

Constants常数

import Foundation

struct Constants {
    struct coinURLs {
        static let allCoinURLs = [
            URL(string: "https://api.coingecko.com/api/v3/coins/bitcoin")!,
            URL(string: "https://api.coingecko.com/api/v3/coins/ethereum")!,
            URL(string: "https://api.coingecko.com/api/v3/coins/litecoin")!
        ]
    }
}

Coin硬币

import Foundation

// MARK: - WelcomeElement
struct Coin: Codable {
    let symbol, name: String
    let marketData: MarketData

    enum CodingKeys: String, CodingKey {
        case symbol, name
        case marketData = "market_data"
    }
    
    static var DefaultCoin = Coin(symbol: " ", name: " ", marketData: MarketData(currentPrice: ["usd": 0.0]))
}

struct Tion: Codable {
    let en: String
    
    enum CodingKeys: String, CodingKey {
        case en
    }
}

struct MarketData: Codable {
    let currentPrice: [String: Double]
    
    enum CodingKeys: String, CodingKey {
        case currentPrice = "current_price"
    }
}

CoinListViewController CoinListViewController

import Foundation
import UIKit

class CoinListViewController: UITableViewController {
    
    private let vm = CoinListViewModel(coin: Coin.DefaultCoin)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
        
        Task {
            await getCoins()
        }
    }
    
    private func configureUI() {
        self.navigationController?.navigationBar.prefersLargeTitles = true
        self.title = "CoinFlip"
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "StockCell")
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        1
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "StockCell", for: indexPath)
        let coin = vm.coin
        
        var content = cell.defaultContentConfiguration()
        content.text = coin.name
        content.secondaryText = "$\(Int(coin.marketData.currentPrice["usd"] ?? 0))"
        cell.contentConfiguration = content
        
        return cell
    }
    
    
    private func getCoins() async {
        await vm.getCoins(url: Constants.coinURLs.allCoinURLs[0])
        print(vm.coin)
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }
}

You can spawn multiple network calls if you make a few changes to your WebService .如果您对WebService进行一些更改,则可以产生多个网络调用。 This will also return a partial result if fetching a coin fails.如果获取硬币失败,这也将返回部分结果。

func getCoins(urls: [URL]) async throws -> [Coin] {
    return try await withThrowingTaskGroup(of: Coin.self, body: { taskGroup in
        var coins = [Coin]()
        
        urls.forEach { url in
            taskGroup.addTask {
                let (data, response) = try await URLSession.shared.data(from: url)
                
                guard
                    let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200
                else { return Coin() }
                
                return try JSONDecoder().decode(Coin.self, from: data)
            }
        }
        
        for try await coin in taskGroup {
            coins.append(coin)
        }
        
        return coins
    })
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM