为什么这种JSON解码会使我的应用程序崩溃?

时间:2018-10-17 20:09:13

标签: swift decodable

我具有由键盘输入触发的功能,如果输入正确,则应用程序可以正确进行;如果不是,则该应用程序实际上死机并且无法更改键盘输入(需要重新启动)。

这就是所谓的函数:

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进行解码,但找到了字符串/数据相反。“,underlyingError:nil)”

天气文档

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!)!

为什么完成处理程序对此有问题?

2 个答案:

答案 0 :(得分:1)

两个问题:

  1. 该错误明确指出cod的类型为String。您可以将所有结构成员声明为非可选。 Openweathermap发送可靠的数据。仅有少数几个可选值(例如,预测API中的Rain

    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 –但是代码(和解码错误)属于weather API。问题中的结构永远无法与此代码/网址一起使用。

答案 1 :(得分:0)

问题是这样的:

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

您只拨打一次makeGetCall()。如果失败,您将永远重复RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1))调用,从而阻塞了主线程。如果要重新尝试makeGetCall直到它起作用,请将其向下移动到repeat块中。

听起来好像还有一个解码问题会阻止makeGetCall()完成,但我将其留给另一个答案。