作为自学Swift的一部分,我正在研究天气应用程序。我目前正在尝试整合天气警报。在序列化API调用返回的数据后,我使用一个名为AlertData的结构来初始化从对weather.gov的API调用返回的数据。或者,至少这是计划。我已经将其我的类建模为从weather.gov请求数据的其他类,但为了获得警报,我需要能够在我的dataTask中发送变量参数。我使用Apple的应用程序开发与Swift的URL扩展(下面的代码),并设置代码以发出用户当前位置的参数,以获取用户当前的警报。
当我尝试在AlertDataController类(下面的代码)中构造对weather.gov的API调用时,我的问题出现了。 Xcode不断抛出不同的错误,我不知道为什么。我想在下面的代码中使用一个保护语句,但是会引发错误"不能强制解包非可选类型的值' [[String:Any]]' "在我的代码中显示。当我在展开后将其作为一个简单的常量赋值时,它也会抛出相同的错误,因为扩展会返回一个可选的URL。
当我直接从guard语句中的字符串构造URL时,相同的代码可以完美地工作,如下所示:
guard let url = URL(string: (baseURL + locationString + stations)) else {
我错过了什么?抛出我的错误的地方是dataTask内部,无论它是如何到达的,变量url都是一个未包装的URL。提前谢谢。
控制器类:
import Foundation
import CoreLocation
struct AlertDataController {
func checkWxAlert(location: CLLocation, completion: @escaping (AlertData?) -> Void) {
let baseURL = URL(string: "https://api.weather.gov/alert")!
let locationString = "\(location.coordinate.latitude),\(location.coordinate.longitude)"
var query = [
"active": "1",
"point": locationString
]
guard let url = baseURL.withQueries(query) else {
completion(nil)
print("Unable to build URL in AlertDataController.checkWxAlert with supplied queries.")
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data,
let rawJSON = try? JSONSerialization.jsonObject(with: data),
let json = rawJSON as? [String: Any],
let featuresDict = json["features"] as? [[String: Any]],
let propertiesArray = featuresDict!["properties"] as? [String: Any] {
Error: Cannot force unwrap value of non-optional type '[[String : Any]]'
let alertData = AlertData(json: propertiesArray)
completion(alertData)
} else {
print("Either no data was returned in AlertDataController.checkWxAlert, or data was not serialized.")
completion(nil)
return
}
}
task.resume()
}
}
网址扩展名:
import Foundation
extension URL {
func withQueries(_ queries: [String: String]) -> URL? {
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.queryItems = queries.flatMap { URLQueryItem(name: $0.0, value: $0.1) }
return components?.url
}
func withHTTPS() -> URL? {
var components = URLComponents(url: self, resolvingAgainstBaseURL: true)
components?.scheme = "https"
return components?.url
}
}
答案 0 :(得分:0)
错误
无法强制展开非可选类型的值' [[String:Any]]'
很清楚。之所以会发生这种情况,是因为在可选的绑定表达式featuresDict
在评估下一个条件时已经解包。
只需删除感叹号
即可 ... let propertiesArray = featuresDict["properties"] as? [String: Any] {
该错误与创建URL的方式完全无关。
答案 1 :(得分:0)
如果featuresDict
确实是一个数组,则不能使用featuresDict["properties"]
语法。带字符串语法的下标仅适用于字典。但是你显然有一系列词典。
您可以遍历featuresDict
数组(我将重命名为featuresArray
以避免混淆),您可以在完成解包后执行此操作。或者,如果只想要与每个词典的properties
键关联的值数组,那么flatMap
可能是一个不错的选择。
例如:
let task = URLSession.shared.dataTask(with: url) { data, _, error in
guard let data = data,
error == nil,
let json = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any],
let featuresArray = json["features"] as? [[String: Any]] else {
print("Either no data was returned in AlertDataController.checkWxAlert, or data was not serialized.")
completion(nil)
return
}
let propertiesArray = featuresArray.flatMap { $0["properties"] }
let alertData = AlertData(json: propertiesArray)
completion(alertData)
}
或者,如果AlertData
期望每个属性本身都是字典,那么您可以这样做:
let propertiesArray = featuresArray.flatMap { $0["properties"] as? [String: Any] }
只需将AlertData
在其数组json
中所期望的任何类型替换为该投射。
或者,如果您只对第一个属性感兴趣,则使用first
而不是flatMap
。