Swift使用Codable解析Open API数据失败,请求技术排查
Swift Codable解析Open-Meteo API无数据问题修复
你的代码主要存在几个核心问题,导致无法获取并解析数据,逐一排查修复如下:
1. URL格式错误(直接导致请求无法发起)
你定义的URL字符串中?后面有大量多余空格,URL(string:)无法识别这种格式的字符串,会返回nil,后续强制解包url!会直接崩溃,根本走不到请求逻辑。
修复:清理URL中的空格,正确的URL应该是:
static let url = URL(string: "https://api.open-meteo.com/v1/forecast?latitude=42.70&longitude=23.32¤t_weather=true")
2. 字段名不匹配(解析失败的核心原因)
Open-Meteo API返回的当前天气字段是current_weather(带下划线),但你的Weather结构体中定义的是currentweather(无下划线),Codable默认按属性名匹配JSON字段,所以完全找不到对应值,解析直接失败。
修复:通过CodingKeys映射字段名,同时遵循Swift规范把结构体名改成大写开头:
struct CurrentWeather: Codable { var temperature: Double var time: String var weathercode: Double var winddirection: Double var windspeed: Double } struct Weather: Codable { var elevation: Double var latitude: Double var longitude: Double var timezone: String var timezone_abbreviation: String var utc_offset_seconds: Int var generationtime_ms: Double var currentWeather: CurrentWeather // 映射JSON下划线字段 enum CodingKeys: String, CodingKey { case elevation, latitude, longitude, timezone, timezone_abbreviation, utc_offset_seconds, generationtime_ms case currentWeather = "current_weather" } }
3. 错误处理过于简陋,无法定位问题
你用try?直接吞掉了解析错误,连哪里错了都不知道。换成do-catch捕获错误,同时增加网络错误、空数据的判断,才能快速定位问题。
修复:完善请求回调中的错误处理:
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in // 先处理网络请求错误 if let error = error { print("请求失败: \(error.localizedDescription)") return } // 确保数据不为空 guard let data = data else { print("未获取到响应数据") return } // 可选:打印原始JSON,方便对比字段 if let jsonStr = String(data: data, encoding: .utf8) { print("原始响应JSON:\n\(jsonStr)") } // 解析数据并捕获错误 do { let weather = try JSONDecoder().decode(Weather.self, from: data) RequestManager.temperature = weather.currentWeather.temperature print("解析成功,当前温度:\(RequestManager.temperature)") } catch { print("解析出错: \(error)") } })
4. 冗余的请求头与不安全的强制解包
- GET请求不需要设置
Content-Type,这个字段是用来标识请求体的数据类型,GET没有请求体,直接删掉这行:request.setValue("application/json", forHTTPHeaderField: "Content-Type") - 不要用
!强制解包url和data,用可选绑定安全处理,避免空值崩溃:
class func getWeatherData() { guard let url = url else { print("URL格式无效") return } var request = URLRequest(url: url) request.httpMethod = "GET" // 后续请求逻辑... }
完整修复后的代码
Codable结构体
import Foundation struct CurrentWeather: Codable { var temperature: Double var time: String var weathercode: Double var winddirection: Double var windspeed: Double } struct Weather: Codable { var elevation: Double var latitude: Double var longitude: Double var timezone: String var timezone_abbreviation: String var utc_offset_seconds: Int var generationtime_ms: Double var currentWeather: CurrentWeather enum CodingKeys: String, CodingKey { case elevation, latitude, longitude, timezone, timezone_abbreviation, utc_offset_seconds, generationtime_ms case currentWeather = "current_weather" } }
RequestManager
import Foundation class RequestManager { static let url = URL(string: "https://api.open-meteo.com/v1/forecast?latitude=42.70&longitude=23.32¤t_weather=true") static var temperature: Double = 0.0 class func getWeatherData() { guard let url = url else { print("URL格式无效") return } var request = URLRequest(url: url) request.httpMethod = "GET" let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in if let error = error { print("请求失败: \(error.localizedDescription)") return } guard let data = data else { print("未获取到响应数据") return } if let jsonStr = String(data: data, encoding: .utf8) { print("原始响应JSON:\n\(jsonStr)") } do { let weather = try JSONDecoder().decode(Weather.self, from: data) RequestManager.temperature = weather.currentWeather.temperature print("解析成功,当前温度:\(RequestManager.temperature)") } catch { print("解析出错: \(error)") } }) task.resume() } }
内容的提问来源于stack exchange,提问作者ALEKS
相关产品推荐
相关产品推荐

