问题描述
我正在设置一个基本应用程序,该应用程序将 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
}
}