如何从 API 调用中添加数据到 Swift 中的 tableviewcell

How to add data from API call to tableviewcell in Swift

我正在设置一个基本应用程序,它 returns 值来自对表视图中单元格的 API 调用。我在网上看到的各种示例将服务文件配置为直接从模型解码。但是,我正在尝试设置我的服务文件,以便它在可能的情况下从 ViewModel 解码。当我 运行 Xcode 中的应用程序时,我没有收到错误,但我也在我的表格视图中看到空白单元格。我对 Swift 中的 API 调用还很陌生。知道如何将其配置为 return API 在仍然使用 viewModel 的同时调用单元格的值吗?请参阅下面的代码:

在我的 WebService 文件中,我按如下方式设置我的 URLSession:

class WebService {
        
    func getStocks(completion: @escaping (([SymbolViewModel]?) -> Void)) {

        guard let url = URL(string: "https://island-bramble.glitch.me/stocks") else {
            fatalError("URL is not correct")
        }

        URLSession.shared.dataTask(with: url) { data, response, error in

            guard let data = data, error == nil else {
                completion(nil)
                return
            }

            let symbols = try? JSONDecoder().decode([SymbolViewModel].self, from: data)
            symbols == nil ? completion(nil) : completion(symbols)

        }.resume()

    }
}

ViewModel 的设置如下:

struct SymbolViewModel:Decodable {
 
    let stockSymbol: Symbol
    
    var symbol: String {
        return self.stockSymbol.symbol.uppercased()
    }
    
    var price: String {
        return String(format: "%.2f", self.stockSymbol.price)
    }
}

模特:

struct Symbol: Decodable {
    let symbol: String
    let price: Double
}

具有 TableView 函数和 TableViewCell 的ViewController:

class ViewController: UIViewController {
    
    let webService = WebService()
    var symbols = [SymbolViewModel]()
    
    @IBOutlet var tableView: UITableView!
        
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let nib = UINib(nibName: "TableViewCell", bundle: nil)
        tableView.register(nib, forCellReuseIdentifier: "TableViewCell")
        tableView.delegate = self
        tableView.dataSource = self
                
        webService.getStocks { symbols in
            if let symbols = symbols {
                DispatchQueue.main.async {
                    self.symbols = symbols
                }
            }
        }
        self.tableView.reloadData()
    }
}

class TableViewCell: UITableViewCell {
    
    @IBOutlet var mySymbol: UILabel!
    @IBOutlet var myPrice: UILabel!

}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    /// tableView functions
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return symbols.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TableViewCell.self), for: indexPath) as! TableViewCell
        
        cell.mySymbol.text = symbols[indexPath.row].symbol
        cell.myPrice.text = symbols[indexPath.row].price

        return cell
    }
}

错误信息:

keyNotFound(CodingKeys(stringValue: "stockSymbol", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"stockSymbol\", intValue: nil) (\"stockSymbol\").", underlyingError: nil))
0 elements

你的模型有误。

首先,如果您声明计算属性,则必须添加 CodingKeys 以防止计算属性被解码。这是错误的主要原因,但还有更多。

JSON是一个(一维)字典数组,对应的模型是

struct StockSymbol : Decodable {
    
    let symbol : String
    let price : Double
    
    private enum CodingKeys : String, CodingKey { case symbol, price }
    
    var priceAsString: String {
        return String(format: "%.2f", price)
    }
}

不需要另一个结构Symbol

解码

do {
    let symbols = try? JSONDecoder().decode([StockSymbol].self, from: data)
    completion(symbols)
} catch {
    print(error)
    completion(nil)
}
 

并且您必须在完成处理程序中重新加载 table 视图

webService.getStocks { symbols in
    if let symbols = symbols {
        DispatchQueue.main.async {
            self.symbols = symbols
            self.tableView.reloadData()
        }
    }
}
   

更复杂的方法是 Result 类型,它 returns 有效数据或错误

class WebService {
    
    func getStocks(completion: @escaping (Result<[StockSymbol],Error>) -> Void) {
        
        guard let url = URL(string: "https://island-bramble.glitch.me/stocks") else {
            fatalError("URL is not correct")
        }
        
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error { completion(.failure(error)); return }
            completion( Result {try JSONDecoder().decode([StockSymbol].self, from: data!)})
        }.resume()
        
    }
}

并称之为

webService.getStocks { [weak self]  result in
    switch result {
        case .success(let symbols):
            DispatchQueue.main.async {
                self?.symbols = symbols
                self?.tableView.reloadData()
            }
        case .failure(let error): print(error) // or show the error to the user
    }
}