我正在尝试从URL获取JSON文件并使用Swift返回内容。但是,代码在以下代码中的let httpResponse = response as! NSHTTPURLResponse
行失败。我在这一行得到一个异常,Xcode进入调试模式。
class func downloadJSONFile()->AnyObject
{
let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
var json:AnyObject = ""
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
}catch {
print("Error with Json: \(error)")
}
}
}
task.resume()
return json
}
我该如何解决这个问题?
答案 0 :(得分:4)
有一些问题:
如果请求中有任何错误,response
将为nil
,因此您强制转换它的尝试将导致致命错误。处理网络响应时,请勿使用强制解包/强制转换。
此处存在一个更深层次的问题,即您尝试从异步运行的方法返回数据。你应该改变你的方法本身不返回任何东西,而是提供一个完成处理程序,通过它可以异步传回相关数据:
class func downloadJSONFile(completionHandler:(AnyObject?) -> ()) {
let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) { data, response, error in
// check for fundamental networking errors
guard error == nil && data != nil else {
print(error)
completionHandler(nil)
return
}
// check to see if status code found, and if so, that it's 200
guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else {
completionHandler(nil)
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:[])
completionHandler(json)
} catch let parseError as NSError {
print("Error with Json: \(parseError)")
completionHandler(nil)
}
}
task.resume()
}
然后使用完成处理程序(或使用尾随闭包语法,如下所示)调用它:
APIClass.downloadJSONFile() { json in
guard json != nil else {
print("there was some problem")
return
}
// now you can use `json` here
dispatch_async(dispatch_get_main_queue()) {
// and if you're doing any model or UI updates, dispatch that back to the main queue
}
}
// but do not use `json` here, as the above runs asynchronously
注意,如果您想将错误信息提供回调用例程,您可以将其更改为还返回错误信息,例如:
enum DownloadError : ErrorType {
case NetworkError(NSError?)
case NotHTTPResponse
case InvalidHTTPResponse(Int)
case JSONError(NSError)
}
class func downloadJSONFile(completionHandler:(AnyObject?, ErrorType?) -> ()) {
let requestURL: NSURL = NSURL(string: "http://www.learnswiftonline.com/Samples/subway.json")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) { data, response, error in
guard error == nil && data != nil else {
completionHandler(nil, DownloadError.NetworkError(error))
return
}
guard let httpResponse = response as? NSHTTPURLResponse else {
completionHandler(nil, DownloadError.NotHTTPResponse)
return
}
guard httpResponse.statusCode == 200 else {
completionHandler(nil, DownloadError.InvalidHTTPResponse(httpResponse.statusCode))
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:[])
completionHandler(json, nil)
} catch let parseError as NSError {
completionHandler(nil, DownloadError.JSONError(parseError))
}
}
task.resume()
}
而且,显然,调用将改为采用两个参数:
APIClass.downloadJSONFile() { json, error in
guard json != nil && error == nil else {
print("there was some problem \(error)")
return
}
// and then it would be like before ...
}
在iOS 9及更高版本中使用NSURLSession
时,它不会允许明文请求(即" http"不允许,只有" https"是,默认情况下)。您可以通过将以下内容添加到info.plist
来强制该应用允许非https请求。有关详细信息,请参阅https://stackoverflow.com/a/31254874/1271826
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>learnswiftonline.com</key>
<dict>
<!--Include to allow subdomains-->
<key>NSIncludesSubdomains</key>
<true/>
<!--Include to allow HTTP requests-->
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<!--Include to specify minimum TLS version-->
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>