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

问题描述

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

在我的 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)
}
 

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

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

更复杂的方式是 Result 类型,它返回有效数据或错误

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
    }
}