【问题标题】:How to change value of a Object Struct in SWIFT 5 [duplicate]如何在 SWIFT 5 中更改对象结构的值 [重复]
【发布时间】:2020-06-28 19:30:38
【问题描述】:

我正在尝试从 JSON 类型中获取价值,将其带到我的 var cryptos 中。但是没有任何改变,var cryptos 仍然为零。甚至更新无效中的加密货币的价值也发生了变化。我已经为这个问题苦苦挣扎了好几个小时。感谢您的回答。 这是我的代码:

var cryptos: Crypto? = nil
override func viewDidLoad() {
    super.viewDidLoad()
    update()
    //I can't print this. ERROR: Unexpectedly found nil while unwrapping an Optional value
    //print(self.cryptos!.data[0].name)
    tableView.delegate = self
    tableView.dataSource = self
 // Do any additional setup after loading the view.
}

@objc func update() {
    if let url = URL(string:"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest") {
                var request = URLRequest(url: url)
                request.addValue("305782b4-...-1835adfe147a", forHTTPHeaderField: "X-CMC_PRO_API_KEY")
                request.httpMethod = "GET"
                let dataTask = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
                    do {
                        do {
                            let response = try JSONDecoder().decode(Crypto.self, from: data!)     
                            self.cryptos = response
                            //but here I can. Why??
                            print(self.cryptos!.data[0].name)
                        } catch { print(error) }
                        }
                    }
                dataTask.resume()
        }
}

这是我的结构:

public struct Crypto : Codable {
    struct data : Codable {
        let id: Int
        let name: String
        let symbol: String
        let cmc_rank: Int
        let slug: String
        struct quote : Codable {
            struct USD : Codable {
                let price: Double
                let volume_24h: Double
                let percent_change_1h: Double
                let percent_change_24h: Double
                let percent_change_7d: Double
            }
            let USD: USD
        }
        let quote: quote
    }
    let data: [data]
}

【问题讨论】:

    标签: ios swift reference unwrap


    【解决方案1】:

    update 异步运行。所以为它提供一个完成处理程序:


    一些建议:

    1. 我会定义一个响应对象来包装有效负载。 data 键不是您需要在模型对象中永久存在的东西:

       public struct ResponseObject<T: Codable>: Codable {
           let data: [T]
       }
      
       struct Crypto: Codable {
           let id: Int
           let name: String
           let symbol: String
           let cmc_rank: Int
           let slug: String
           struct Quote : Codable {
               struct USD : Codable {
                   let price: Double
                   let volume_24h: Double
                   let percent_change_1h: Double
                   let percent_change_24h: Double
                   let percent_change_7d: Double
               }
               let USD: USD
           }
           let quote: Quote
       }
      

      我倾向于使用上述通用模式,因此您可以在其他地方重用这个ResponseObject 模式。

    2. 我会给update 一个使用Result 类型的完成处理程序:

       @discardableResult
       func update(completion: @escaping (Result<[Crypto], Error>) -> Void) -> URLSessionTask {
           let url = URL(string:"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest")!
           var request = URLRequest(url: url)
           request.addValue("305782b4-...-1835adfe147a", forHTTPHeaderField: "X-CMC_PRO_API_KEY")
           let task = URLSession.shared.dataTask(with: request) { data, response, error in
               guard let responseData = data, error == nil else {
                   DispatchQueue.main.async { completion(.failure(error ?? NetworkError.unknownError(data, response))) }
                   return
               }
      
               do {
                   let response = try JSONDecoder().decode(ResponseObject<Crypto>.self, from: responseData)
                   DispatchQueue.main.async { completion(.success(response.data)) }
               } catch {
                   DispatchQueue.main.async { completion(.failure(error)) }
               }
           }
           task.resume()
           return task
       }
      

      在哪里

       enum NetworkError: Error {
           case unknownError(Data?, URLResponse?)
       }
      

      仅供参考,除了完成处理程序闭包之外,我还删除了强制展开运算符 (!)。

      我也会让它返回URLSessionTask(以防调用者在未来某个日期希望能够以任何理由取消请求)。通过将其设为@discardableResult,这意味着您不需要调用者对返回的值执行任何操作,但您为未来敞开了大门。

    3. viewDidLoad 然后可以根据需要使用它来重新加载表:

       var cryptos: [Crypto] = []
      
       override func viewDidLoad() {
           super.viewDidLoad()
      
           update() { result in
               switch result {
               case .failure(let error):
                   print(error)
      
               case .success(let cryptos):
                   self.cryptos = cryptos
                   self.tableView.reloadData()
               }
           }
       }
      

      注意,模型对象的更新和表的重新加载都是在这个完成处理程序中完成的,它已被分派到主队列。所有 UI 和模型更新都应在主线程上进行。

    【讨论】:

    • 我真的很感激。试过你的建议。但是,我如何获得这个值?示例:cell.nameCrypto.text = cryptos.data[indexPath.row].name cell.nameCrypto.text = cryptos[indexPath.row].data.name
    • 我试过这个,它可以按我的意愿工作。 let cell = tableView.dequeueReusableCell(withIdentifier: "cellID") as! CryptoTableViewCell update() { result in switch result { case .failure(let error): print(error) case .success(let cryptos): self.cryptos = cryptos cell.nameCrypto.text = cryptos.data[indexPath.row].name } } return cell
    • 更改为 var cryptos: Crypto? 和 let response = try JSONDecoder().decode(Crypto.self, from: responseData) DispatchQueue.main.async { completion(.success(response)) 。非常感谢。当我有足够的声誉时,我会投票
    • 我对您的模型进行了修改,它将是cell.label.text = cryptos[indexPath.row].name。这个data 键存在于您的 JSON 中,但不属于您的模型。它只用于 JSON 解析(这就是我将其归为 ResponseObject 泛型的原因)。所以,不,我修改后的模型和修改后的 update 方法应该保持我写的样子。
    【解决方案2】:

    向函数添加一个完成块,因为它是一个异步任务。

    override func viewDidLoad() {
        super.viewDidLoad()
        update {
            print(self.cryptos?.data[0].name ?? "")
        }
        //...
    }
    @objc func update(completion: @escaping () -> Void) {
        if let url = URL(string:"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest") {
            //...
            let dataTask = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
                do {
                    let response = try JSONDecoder().decode(Crypto.self, from: data!)
                    self.cryptos = response
                    print(self.cryptos!.data[0].name)
                    completion()
                } catch {
                    print(error)
                    completion()
                }
            }
            dataTask.resume()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-04-04
      • 2023-03-24
      • 1970-01-01
      • 2020-09-01
      • 2020-02-04
      • 2021-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多