首页 > 解决方案 > 为什么这个 JSON 解码会使我的应用程序崩溃?

问题描述

我有这个由键盘输入触发的功能,如果输入正确,应用程序会正确运行;如果不是,应用程序本质上会冻结并且无法更改键盘输入(需要重新启动)。

这就是所谓的函数:

override func viewDidLoad() {
    super.viewDidLoad()

    makeGetCall()
    repeat{
        RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1))
    }while !done

被调用的函数:

func makeGetCall() {
    let jsonUrlString = "http://api.openweathermap.org/data/2.5/weather?q=" + city + ",us&appid=f0d10597634568abee813f68138452fd&units=imperial"
    guard let url = URL(string: jsonUrlString) else {
        print("Error: cannot create URL")
        return

    }

    URLSession.shared.dataTask(with: url) { (data, response, err) in
        guard let data = data else {
            print("Error: did not receive data")
            return
        }
        do {

            self.document = try JSONDecoder().decode(WeatherDocument.self, from: data)
            self.done = true

            print(self.document!)
            print("========================INIT===============================")
            print(self.document?.main?.temp! ?? "No temp")
            print(self.document?.name! ?? "No temp")
            print(self.document?.weather![0].description ?? "No info")
            print(self.document?.wind?.speed ?? "No wind")
            print("==========================END===============================")
            print(self.document?.weather![0].main ?? "No main info")
        } catch let jsonErr {
            print("Error serializing json:", jsonErr)
        }


        }.resume()
}

任何想法为什么会发生这种情况?

这是控制台中显示的错误消息:

“错误序列化 json: typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "cod", intValue: nil)], debugDescription: "预期解码 Double 但找到了一个字符串/数据。" ,基础错误:无)“

天气文件

struct WeatherDocument: Decodable {
let coord: Coordinates?
let weather: [Weather]?
let base: String?
let main: Main?
let visibility: Double?
let wind: Wind?
let clouds: Clouds?
let dt: Double?
let sys: Sys?
let id: Double?
let name: String?
let cod: Double?
}

现在应用程序在我所做的以下语句处执行断点:

    let tempe = (self.document?.main?.temp!)!
    let humiditye = (self.document?.main?.humidity!)!
    let pressurePow = (self.document?.main?.pressure!)! * 0.295300 * 0.10

    let tempeMax = (self.document?.main?.temp_max!)! - 273.15
    let tempeMin = (self.document?.main?.temp_min!)! - 273.15
    //let clouding = (self.precip?.threeHours!)!
    let name = (self.document?.name!)!

为什么完成处理程序有这个问题?

标签: swiftdecodable

解决方案


两个问题:

  1. 该错误清楚地表明类型cod是字符串。您可以将所有结构成员声明为非可选的。Openweathermap 发送可靠的数据。只有少数值(例如Rain在预测 API 中)是可选的

    let cod : String
    
  2. 永远不要使用这样的重复循环。你阻塞线程。使用完成处理程序。强烈建议使用URLComponents隐式添加百分比编码。

    var city = "New York,us"
    let apiKey = <your api key>
    
    ...
    
    func makeGetCall(completion: @escaping (WeatherDocument?, Error?)->Void) {
        var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/weather")!
        let queryItems = [URLQueryItem(name: "q", value: city),
                          URLQueryItem(name: "appid", value: apiKey),
                          URLQueryItem(name: "units", value: "imperial")]
    
        guard let url = urlComponents.url else {
            print("Error: cannot create URL")
            return
        }
    
        URLSession.shared.dataTask(with: url) { (data, response, err) in
            guard let data = data else {
                print("Error: did not receive data")
                completion(nil, error!)
            }
            do {
    
                let document = try JSONDecoder().decode(WeatherDocument.self, from: data)
    
                print(document)
                print("========================INIT===============================")
                print(document.main.temp) // main and temp can be non-optional
                print(document.name) // name can be non-optional
                print(document.weather[0].description) // weather and description can be non-optional
                print(document.wind.speed) // wind and speed can be non-optional
                print("==========================END===============================")
                print(document.weather[0].main) // weather and main can be non-optional
                completion(document, nil)
            } catch {
                print("Error serializing json:", error)
                completion(nil, error)
            }
    
    
        }.resume()
    }
    

    并称之为

    makeGetCall() { doc, error in
        if let doc = doc {
           self.document = doc
        } else {
          print(error!)
        }
    }
    

PS:您混淆了预测天气API。您的结构属于预测 API -cod实际上在哪里Int- 但代码(和解码错误)属于天气 API。问题中的结构永远不会与此代码/ URL 一起使用。


推荐阅读