API 调用期间 Swift valueNotFound 的问题
Issues with Swift valueNotFound during API Call
我正在寻求有关 Swift 项目的帮助。我正在构建一个从 API 中提取航空天气数据的应用程序,这种情况很常见:
用户需要机场气象站 KDAB 的数据 - 当前报告说:
- 风力 10 节
- 散云 2500 英尺
- 能见度 10 SM
- 小雨
用户需要来自机场气象站 KJAX 的数据 - 当前报告说:
- 风力 16 节
- 阵风 24 节
- 碎云 1400 英尺
- 散云 1900 英尺
- 2400 英尺云少
在这个简单的例子中,您可能会注意到,在此报告期间没有为 KJAX 提供阵风数据,也没有为 KDAB 指定“特殊天气”(即雨、霾、雾)。我的应用程序需要能够处理“无”或未提供的数据,而不仅仅是告诉我存在 valueNotFound 或索引超出范围。
这是 API 文档:https://avwx.docs.apiary.io/#reference/0/metar/get-metar-report
这是我的代码:
import Foundation
struct WeatherManager {
let weatherURL = "https://avwx.rest/api/metar/"
func fetchWeather (stationICAO: String) {
let urlString = "\(weatherURL)\(stationICAO)?token=OVi45FiTDo1LmyodShfOfoizNe5m9wyuO6Mkc95AN-c"
performRequest(urlString: urlString)
}
func performRequest (urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
self.parseJSON(weatherData: safeData)
}
}
task.resume()
print(urlString)
}
}
func parseJSON(weatherData: Data) {
do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let lowCloudsType = decodedData.clouds[0].type
let midCloudsType = decodedData.clouds[1].type
let highCloudsType = decodedData.clouds[2].type
let lowCloudsAlt = decodedData.clouds[0].altitude
let midCloudsAlt = decodedData.clouds[1].altitude
let highCloudsAlt = decodedData.clouds[2].altitude
let reportingStationVar = decodedData.station
let windGustValue = decodedData.wind_gust.value
let windSpeedValue = decodedData.wind_speed.value
let windDirectionValue = decodedData.wind_direction.value
let visibilityValue = decodedData.visibility.value
let flightRulesValue = decodedData.flight_rules
let weather = WeatherModel(lowestCloudsType: lowCloudsType, lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType, middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType, highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)
print(weather.flightConditions)
} catch {
print(error)
}
}
}
import Foundation
struct WeatherModel {
let lowestCloudsType: String
let lowestCloudsAlt: Int
let middleCloudsType: String
let middleCloudsAlt: Int
let highestCloudsType: String
let highestCloudsAlt: Int
let reportingStation: String
let windGust: Int
let windSpeed: Int
let windDirection: Int
let visibility: Int
let flightRules: String
var flightConditions: String {
switch flightRules {
case "VFR":
return "green"
case "MVFR":
return "blue"
case "IFR":
return "red"
case "LIFR":
return "purple"
default:
return "gray"
}
}
}
最后一个:
import Foundation
struct WeatherData: Decodable {
let clouds: [Clouds]
let flight_rules: String
let remarks: String
let wind_speed: WindSpeed
let wind_gust: WindGust
let wind_direction: WindDirection
let visibility: Visibility
let station: String
}
struct Clouds: Decodable {
let type: String
let altitude: Int
}
struct WindSpeed: Decodable {
let value: Int
}
struct WindGust: Decodable {
let value: Int
}
struct WindDirection: Decodable {
let value: Int
}
struct Visibility: Decodable {
let value: Int
}
根据我玩的是什么,当我进入一个没有我需要能够呈现给如果天气服务报告了用户。
2020-09-22 02:47:58.930421-0400 AvWx Pro[66612:4483807] libMobileGestalt MobileGestaltCache.c:38: No persisted cache on this platform.
KDAB
https://avwx.rest/api/metar/KDAB?token=(mySecretToken)
2020-09-22 02:48:02.943231-0400 AvWx Pro[66612:4483809] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
valueNotFound(Swift.KeyedDecodingContainer<AvWx_Pro.WindGust.(unknown context at 53fb3b8).CodingKeys>,
Swift.DecodingError.Context(codingPath:
[CodingKeys(stringValue: "wind_gust", intValue: nil)],
debugDescription: "Cannot get keyed decoding container
-- found null value instead.", underlyingError: nil))
当我使用没有报告所有三个可能的云层的机场时出现不同的错误:
2020-09-22 03:06:02.398628-0400 AvWx Pro[66736:4497432] libMobileGestalt MobileGestaltCache.c:38: No persisted cache on this platform.
KJAX
https://avwx.rest/api/metar/KJAX?token=(mySecretKey)
2020-09-22 03:06:07.955064-0400 AvWx Pro[66736:4497429] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
Fatal error: Index out of range: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1200.2.22.2/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 444
2020-09-22 03:06:08.908826-0400 AvWx Pro[66736:4497429] Fatal error: Index out of range: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1200.2.22.2/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 444
(lldb)
我现在已经花了几个小时尝试我在网上找到的各种解决方案,包括使用可选和强制展开、使用 guard let、使用 if let 和其他一些。我现在很迷茫。
我是这个平台的新手(作为发帖人),非常感谢任何人提供的任何见解!提前感谢您的帮助。
答案是使用 Optionals
考虑下面的游乐场示例,年龄和位置属性是可选的,JSON 中缺少位置 属性。
import Foundation
struct TestModel: Decodable {
let name: String
let age: Int?
let location: String?
enum CodingKeys: String, CodingKey {
case name
case age
case location
}
}
let jsonData = """
{
"name": "John Smith",
"age": 28
}
""".data(using: .utf8)!
let result = try JSONDecoder().decode(TestModel.self, from: jsonData)
print(result)
输出
TestModel(name: "John Smith", age: Optional(28), location: nil)
为避免解码时崩溃 json 您应该正确配置结构并检查访问值之前的字段:
- 可选字段
[CodingKeys(stringValue: "wind_gust", intValue: nil)],
debugDescription: "Cannot get keyed decoding container -- found null
value instead.", underlyingError: nil))
wind_gust
can be empty so you should make it optional:
如果响应中的字段可以为空或 null,您应该在结构中将其设为可选,例如:
struct WeatherData: Decodable {
...
let wind_gust: WindGust?
...
}
然后在您的代码中,如果 wind_gust 存在,则只需使用可选绑定来提取一个值:
if let value = decodedData.wind_gust?.value {
print(value)
}
- 数组
Fatal error: Index out of range
您必须始终在访问项目之前检查数组的边界,例如:
let clouds = decodedData.clouds
let lowCloudsType = clouds.count > 0 ? clouds[0].type : nil
let midCloudsType = clouds.count > 1 ? clouds[1].type : nil
let highCloudsType = clouds.count > 2 ? clouds[2].type : nil
if let low = lowCloudsType, let mid = midCloudsType, let high = highCloudsType {
print(low)
print(mid)
print(high)
}
我正在寻求有关 Swift 项目的帮助。我正在构建一个从 API 中提取航空天气数据的应用程序,这种情况很常见:
用户需要机场气象站 KDAB 的数据 - 当前报告说:
- 风力 10 节
- 散云 2500 英尺
- 能见度 10 SM
- 小雨
用户需要来自机场气象站 KJAX 的数据 - 当前报告说:
- 风力 16 节
- 阵风 24 节
- 碎云 1400 英尺
- 散云 1900 英尺
- 2400 英尺云少
在这个简单的例子中,您可能会注意到,在此报告期间没有为 KJAX 提供阵风数据,也没有为 KDAB 指定“特殊天气”(即雨、霾、雾)。我的应用程序需要能够处理“无”或未提供的数据,而不仅仅是告诉我存在 valueNotFound 或索引超出范围。
这是 API 文档:https://avwx.docs.apiary.io/#reference/0/metar/get-metar-report
这是我的代码:
import Foundation
struct WeatherManager {
let weatherURL = "https://avwx.rest/api/metar/"
func fetchWeather (stationICAO: String) {
let urlString = "\(weatherURL)\(stationICAO)?token=OVi45FiTDo1LmyodShfOfoizNe5m9wyuO6Mkc95AN-c"
performRequest(urlString: urlString)
}
func performRequest (urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
self.parseJSON(weatherData: safeData)
}
}
task.resume()
print(urlString)
}
}
func parseJSON(weatherData: Data) {
do {
let decoder = JSONDecoder()
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let lowCloudsType = decodedData.clouds[0].type
let midCloudsType = decodedData.clouds[1].type
let highCloudsType = decodedData.clouds[2].type
let lowCloudsAlt = decodedData.clouds[0].altitude
let midCloudsAlt = decodedData.clouds[1].altitude
let highCloudsAlt = decodedData.clouds[2].altitude
let reportingStationVar = decodedData.station
let windGustValue = decodedData.wind_gust.value
let windSpeedValue = decodedData.wind_speed.value
let windDirectionValue = decodedData.wind_direction.value
let visibilityValue = decodedData.visibility.value
let flightRulesValue = decodedData.flight_rules
let weather = WeatherModel(lowestCloudsType: lowCloudsType, lowestCloudsAlt: lowCloudsAlt, middleCloudsType: midCloudsType, middleCloudsAlt: midCloudsAlt, highestCloudsType: highCloudsType, highestCloudsAlt: highCloudsAlt, reportingStation: reportingStationVar, windGust: windGustValue, windSpeed: windSpeedValue, windDirection: windDirectionValue, visibility: visibilityValue, flightRules: flightRulesValue)
print(weather.flightConditions)
} catch {
print(error)
}
}
}
import Foundation
struct WeatherModel {
let lowestCloudsType: String
let lowestCloudsAlt: Int
let middleCloudsType: String
let middleCloudsAlt: Int
let highestCloudsType: String
let highestCloudsAlt: Int
let reportingStation: String
let windGust: Int
let windSpeed: Int
let windDirection: Int
let visibility: Int
let flightRules: String
var flightConditions: String {
switch flightRules {
case "VFR":
return "green"
case "MVFR":
return "blue"
case "IFR":
return "red"
case "LIFR":
return "purple"
default:
return "gray"
}
}
}
最后一个:
import Foundation
struct WeatherData: Decodable {
let clouds: [Clouds]
let flight_rules: String
let remarks: String
let wind_speed: WindSpeed
let wind_gust: WindGust
let wind_direction: WindDirection
let visibility: Visibility
let station: String
}
struct Clouds: Decodable {
let type: String
let altitude: Int
}
struct WindSpeed: Decodable {
let value: Int
}
struct WindGust: Decodable {
let value: Int
}
struct WindDirection: Decodable {
let value: Int
}
struct Visibility: Decodable {
let value: Int
}
根据我玩的是什么,当我进入一个没有我需要能够呈现给如果天气服务报告了用户。
2020-09-22 02:47:58.930421-0400 AvWx Pro[66612:4483807] libMobileGestalt MobileGestaltCache.c:38: No persisted cache on this platform.
KDAB
https://avwx.rest/api/metar/KDAB?token=(mySecretToken)
2020-09-22 02:48:02.943231-0400 AvWx Pro[66612:4483809] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
valueNotFound(Swift.KeyedDecodingContainer<AvWx_Pro.WindGust.(unknown context at 53fb3b8).CodingKeys>,
Swift.DecodingError.Context(codingPath:
[CodingKeys(stringValue: "wind_gust", intValue: nil)],
debugDescription: "Cannot get keyed decoding container
-- found null value instead.", underlyingError: nil))
当我使用没有报告所有三个可能的云层的机场时出现不同的错误:
2020-09-22 03:06:02.398628-0400 AvWx Pro[66736:4497432] libMobileGestalt MobileGestaltCache.c:38: No persisted cache on this platform.
KJAX
https://avwx.rest/api/metar/KJAX?token=(mySecretKey)
2020-09-22 03:06:07.955064-0400 AvWx Pro[66736:4497429] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
Fatal error: Index out of range: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1200.2.22.2/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 444
2020-09-22 03:06:08.908826-0400 AvWx Pro[66736:4497429] Fatal error: Index out of range: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1200.2.22.2/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 444
(lldb)
我现在已经花了几个小时尝试我在网上找到的各种解决方案,包括使用可选和强制展开、使用 guard let、使用 if let 和其他一些。我现在很迷茫。
我是这个平台的新手(作为发帖人),非常感谢任何人提供的任何见解!提前感谢您的帮助。
答案是使用 Optionals
考虑下面的游乐场示例,年龄和位置属性是可选的,JSON 中缺少位置 属性。
import Foundation
struct TestModel: Decodable {
let name: String
let age: Int?
let location: String?
enum CodingKeys: String, CodingKey {
case name
case age
case location
}
}
let jsonData = """
{
"name": "John Smith",
"age": 28
}
""".data(using: .utf8)!
let result = try JSONDecoder().decode(TestModel.self, from: jsonData)
print(result)
输出
TestModel(name: "John Smith", age: Optional(28), location: nil)
为避免解码时崩溃 json 您应该正确配置结构并检查访问值之前的字段:
- 可选字段
[CodingKeys(stringValue: "wind_gust", intValue: nil)], debugDescription: "Cannot get keyed decoding container -- found null value instead.", underlyingError: nil))
wind_gust
can be empty so you should make it optional:
如果响应中的字段可以为空或 null,您应该在结构中将其设为可选,例如:
struct WeatherData: Decodable {
...
let wind_gust: WindGust?
...
}
然后在您的代码中,如果 wind_gust 存在,则只需使用可选绑定来提取一个值:
if let value = decodedData.wind_gust?.value {
print(value)
}
- 数组
Fatal error: Index out of range
您必须始终在访问项目之前检查数组的边界,例如:
let clouds = decodedData.clouds
let lowCloudsType = clouds.count > 0 ? clouds[0].type : nil
let midCloudsType = clouds.count > 1 ? clouds[1].type : nil
let highCloudsType = clouds.count > 2 ? clouds[2].type : nil
if let low = lowCloudsType, let mid = midCloudsType, let high = highCloudsType {
print(low)
print(mid)
print(high)
}