You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift UIKit应用JSON解码失败求助:所有属性均为nil

OpenWeatherMap JSON解码后所有属性为nil的问题排查与解决

问题描述

在UIKit应用中调用OpenWeatherMap API获取天气数据,解码返回的JSON后,Response实例的所有属性均为nil,但直接访问API链接可获取正确的JSON数据。

解码代码

override func viewDidLoad() {
    super.viewDidLoad()
    cidadelabel.text = city!
    downloadJSON {
        print("success")
    }
    print(result.main?.temp!)
    // var temperatura = result?.main?.temp
    // temperatura = temperatura! - 273.15
    // temperatureLabel.text = "\(String(temperatura!))°C"
}

func downloadJSON(completed: @escaping () -> ()){
    let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?appid=******d&q=" + city!)
    URLSession.shared.dataTask(with: url!) {
        data, response, error in
        if error == nil{
            do{
                print(data!)
                print(response!)
                
                self.result = try JSONDecoder().decode(Response.self, from: data!)
                
                DispatchQueue.main.async {
                    completed()
                }
            }
            catch {
                print("error fetching data from the api")
                print(String(describing: error))
            }
        }
    }.resume()
}

原JSON接收结构体

import Foundation

struct Response: Codable {
    var coord: Coord?
    var weather: [WeatherBase]?
    var main: Main?
    var visibility: Int?
    var wind: Wind?
    var rain: Rain?
    var clouds: Clouds?
    var dt: Int?
    var sys: Sys?
    var timezone: Int?
    var id: Int?
    var name: String?
    var cod: Int?
}

struct Coord: Codable {
    var lon: Double?
    var lat: Double?
}

struct WeatherBase: Codable {
    var id: String { _id! }
    private var _id: String?
    var base: String?
    
    mutating func prepare() {
        _id = _id ?? UUID().uuidString
    }
}

struct Weather: Codable {
    var id: Int?
    var main: String?
    var description: String?
    var icon: String?
}

struct Main: Codable {
    var temp: Double?
    var feels_like: Double?
    var temp_min: Double?
    var temp_max: Double?
    var pressure: Int?
    var humidity: Int?
}

struct Wind: Codable {
    var speed: Double?
    var deg: Int?
}

struct Rain: Codable{
    var umh: Int?
}

struct Clouds: Codable {
    var all: Int?
}

struct Sys: Codable {
    var type: Int?
    var id: Int?
    var country: String?
    var sunrise: Int?
    var sunset: Int?
}

API返回的JSON

{"coord":{"lon":-35.7353,"lat":-9.6658},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"base":"stations","main":{"temp":302.84,"feels_like":305.59,"temp_min":302.84,"temp_max":302.84,"pressure":1011,"humidity":61},"visibility":10000,"wind":{"speed":7.2,"deg":90},"clouds":{"all":20},"dt":1672689129,"sys":{"type":1,"id":8413,"country":"BR","sunrise":1672646788,"sunset":1672692412},"timezone":-10800,"id":3395981,"name":"Maceió","cod":200}

问题现象

解码后得到的Response实例所有属性均为nil:

Response(coord: nil, weather: nil, main: nil, visibility: nil, wind: nil, rain: nil, clouds: nil, dt: nil, sys: nil, timezone: nil, id: nil, name: nil, cod: nil)


解决步骤

1. 修正结构体类型匹配错误

  • Response中的weather字段声明为[WeatherBase]?,但API返回的是符合Weather结构体的数组,将其改为[Weather]?,并删除无用的WeatherBase结构体。
  • Rain结构体的字段名错误:OpenWeatherMap的rain字段为1h或3h,而非umh,当前返回中无rain数据,可暂时保留可选类型或修正字段名。

修正后的核心结构体:

struct Response: Codable {
    var coord: Coord?
    var weather: [Weather]?
    var main: Main?
    var visibility: Int?
    var wind: Wind?
    var rain: Rain?
    var clouds: Clouds?
    var dt: Int?
    var sys: Sys?
    var timezone: Int?
    var id: Int?
    var name: String?
    var cod: Int?
}

// 保留正确的Weather结构体,删除WeatherBase
struct Weather: Codable {
    var id: Int?
    var main: String?
    var description: String?
    var icon: String?
}

// 修正Rain结构体(如需处理降雨数据)
struct Rain: Codable{
    enum CodingKeys: String, CodingKey {
        case oneHour = "1h"
    }
    var oneHour: Double?
}

2. 调整解码后逻辑的执行时机

viewDidLoad中直接调用print(result.main?.temp!)是在异步请求完成前执行的,此时result尚未被赋值,需将所有依赖result的逻辑放到completed闭包内:

override func viewDidLoad() {
    super.viewDidLoad()
    cidadelabel.text = city!
    downloadJSON {
        print("success")
        // 此处访问result才是有效的
        if let temp = self.result.main?.temp {
            let temperatura = temp - 273.15
            self.temperatureLabel.text = String(format: "%.1f°C", temperatura)
        }
    }
}

3. 避免强制解包

代码中大量使用!强制解包,容易导致崩溃,建议使用可选绑定处理可选值:

// 安全创建URL
guard let cityName = city, 
      let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?appid=******d&q=" + cityName) else {
    print("无效的城市名或URL")
    return
}

4. 查看精准解码错误

修改catch块的打印逻辑,获取更详细的解码错误信息,便于定位问题:

catch {
    print("JSON解码失败:\(error)")
    if let decodingError = error as? DecodingError {
        print("解码错误详情:\(decodingError.localizedDescription)")
    }
}

内容的提问来源于stack exchange,提问作者Caio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:45:19