SwiftUI解析Yandex天气API JSON响应失败问题咨询
问题分析与修复方案
核心错误原因
当前解码失败的直接原因是**info结构体的字段与JSON不匹配**:API返回的JSON中info对象包含的是_h字段,但代码里定义的是h,导致Swift找不到对应key,触发keyNotFound错误。除此之外,还有多处类型不匹配、非可选字段缺失的问题,逐一修复如下:
具体修复步骤
1. 修正info结构体的字段映射
将info结构体中的h字段改为_h,对应JSON里的"_h":false:
struct info: Codable, Identifiable { let id = UUID() let n: Bool let geoid: Int let url: String let lat, lon: Int let tzinfo: tzinfo let def_pressure_mm, def_pressure_pa: Int let slug: String let zoom: Int let nr, ns, nsr, p: Bool let f, _h: Bool // 替换原h字段为_h }
2. 修正geo_object结构体的可选类型
API返回的geo_object中district和locality是null,需将其改为可选类型:
struct geo_object: Codable, Identifiable { let id = UUID() let district: String? // 改为可选 let locality: country? // 改为可选,适配JSON中的null值 let province, country: country }
3. 修正fact结构体的类型与缺失字段
返回的JSON中fact存在浮点型字段,且缺少accum_prec字段,需调整类型并设置可选:
struct fact : Codable, Identifiable { let id = UUID() let obs_time, uptime, temp, feels_like: Int let icon: icon let condition: condition let cloudness, prec_type, prec_prob: Int let prec_strength: Double // JSON中是0.25,原Int类型会报错 let is_thunder: Bool let wind_speed: Double let wind_dir: wind_dir let pressure_mm, pressure_pa, humidity: Int let daytime: daytime let polar: Bool let season, source: String let accum_prec: [String: Double]? // 改为可选,JSON中无此字段 let soil_moisture: Double let soil_temp, uv_index: Int let wind_gust: Double // JSON中是6.6,原Int类型会报错 }
4. 修正forecast结构体的缺失字段
返回的JSON片段中forecast仅包含部分字段,将未返回的字段改为可选:
struct forecast: Codable, Identifiable { let id = UUID() let date: String let date_ts, week: Int let sunrise: String let sunset, rise_begin, set_end: String? // 改为可选 let moon_code: Int? // 改为可选 let moon_text: String? // 改为可选 let parts: parts? // 改为可选 let hours: [hour]? // 改为可选 let biomet: biomet? }
5. 移除强制解包,添加错误处理
代码中try!和data!会直接触发崩溃,改用do-catch处理错误:
class WeatherApi { func getPost(completion: @escaping (Post?) -> ()){ guard let url = URL(string: "https://api.weather.yandex.ru/v2/forecast?lat=50&lon=37") else { completion(nil) return } print("kek") var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue("[key here]", forHTTPHeaderField: "X-Yandex-API-Key") URLSession.shared.dataTask(with: request){(data, _, error) in guard let data = data, error == nil else { print("请求错误:\(error?.localizedDescription ?? "未知错误")") completion(nil) return } do { let post = try JSONDecoder().decode(Post.self, from: data) print(post) DispatchQueue.main.async { completion(post) } } catch { print("解码错误:\(error)") completion(nil) } } .resume() } }
额外建议
- 结构体名遵循Swift命名规范,首字母大写(如
info改为Info、fact改为Fact),提升代码可读性。 - 使用
JSONDecoder的蛇形转驼峰策略,简化字段命名:
这样可以将let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCasenow_dt改为nowDt、geo_object改为geoObject,更符合Swift风格。
内容的提问来源于stack exchange,提问作者perfect05
相关产品推荐
相关产品推荐

