Swift:如何在异步 urlsession 函数中返回一个值?
Posted
技术标签:
【中文标题】Swift:如何在异步 urlsession 函数中返回一个值?【英文标题】:Swift: How do I return a value within an asynchronous urlsession function? 【发布时间】:2014-11-22 18:42:24 【问题描述】:如您所见,我正在接收一个 JSON 文件,使用 SwiftyJSON 对其进行解析,并尝试返回 totalTime,但它不会让我这样做。我该怎么做?
func googleDuration(origin: String, destination: String) -> Int
// do calculations origin and destiantion with google distance matrix api
let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix;
println(urlAsString);
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url, completionHandler: data, response, error -> Void in
if error != nil
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
println("parsing JSON");
let json = JSON(data: data);
if (json["status"].stringValue == "OK")
if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue
println(totalTime);
)
task.resume();
【问题讨论】:
顺便说一句,不只是用+
替换空格,您可以考虑进行适当的百分比转义,如此处所述***.com/a/24888789/1271826
【参考方案1】:
您应该添加自己的completionHandler
闭包参数并在任务完成时调用它:
func googleDuration(origin: String, destination: String, completionHandler: (Int?, NSError?) -> Void ) -> NSURLSessionTask
// do calculations origin and destiantion with google distance matrix api
let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix
println(urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url) data, response, error -> Void in
if error != nil
// If there is an error in the web request, print it to the console
// println(error.localizedDescription)
completionHandler(nil, error)
return
//println("parsing JSON");
let json = JSON(data: data)
if (json["status"].stringValue == "OK")
if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue
// println(totalTime);
completionHandler(totalTime, nil)
return
let totalTimeError = NSError(domain: kAppDomain, code: kTotalTimeError, userInfo: nil) // populate this any way you prefer
completionHandler(nil, totalTimeError)
let jsonError = NSError(domain: kAppDomain, code: kJsonError, userInfo: nil) // again, populate this as you prefer
completionHandler(nil, jsonError)
task.resume()
return task
如果调用者希望能够取消任务,我也会返回 NSURLSessionTask
。
不管怎样,你会这样称呼它:
googleDuration(origin, destination: destination) totalTime, error in
if let totalTime = totalTime
// use totalTime here
else
// handle error
【讨论】:
有什么办法可以使这个非异步的?我需要它等到解析出响应,但我不知道该怎么做。您的解决方案是对的,我认为我的问题是错误的 你可以,但它严重不受欢迎。这是一个可怕的用户体验,如果你在错误的时间这样做,看门狗进程可能会立即杀死你的应用程序。正确的解决方案是禁用 UI,显示微调器(在 ios 中,UIActivityIndicatorView
),并在请求完成时反转该过程。你真的应该接受异步模式而不是反对它。
顺便说一句,人们通常会将这些完成处理程序分派到主队列(因为调用者通常会进行 UI 更新)。如果您希望我更新答案以向您展示如何做到这一点,请告诉我。如果您精通 GCD,这可能是不言自明的,但如果您不精通,我很乐意向您展示。
如果我这样做,它会将 timeLabel 设置为 -999,而不是我想要的。var tripTime = -999; googleDuration(startLabel.text, destination: endLabel.text) totalTime, error in if let totalTime = totalTime // use totalTime here tripTime = totalTime; println(tripTime); println("Returning: "); println(tripTime); timeLabel.text = String(tripTime);
是的,所以它是self.timerLabel.text = ...
。你把它分派到主队列了吗?【参考方案2】:
另一个例子:
class func getExchangeRate(#baseCurrency: String, foreignCurrency:String, completion: ((result:Double?) -> Void)!)
let baseURL = kAPIEndPoint
let query = String(baseCurrency)+"_"+String(foreignCurrency)
var finalExchangeRate = 0.0
if let url = NSURL(string: baseURL + query)
NSURLSession.sharedSession().dataTaskWithURL(url) data, response, error in
if ((data) != nil)
let jsonDictionary:NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSDictionary
if let results = jsonDictionary["results"] as? NSDictionary
if let queryResults = results[query] as? NSDictionary
if let exchangeRate = queryResults["val"] as? Double
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0))
dispatch_async(dispatch_get_main_queue())
completion(result: exchangeRate)
else
completion(result: nil)
.resume()
呼叫:
Currency.getExchangeRate(baseCurrency: "USD", foreignCurrency: "EUR") (result) -> Void in
if let exchangeValue = result
print(exchangeValue)
【讨论】:
【参考方案3】:另一个例子:
func getJason(url: NSURL, completionHandler: (String?, NSError?) -> Void ) -> NSURLSessionTask
var finalData: String!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) (data, response, error) -> Void in
if error != nil
completionHandler(nil, error)
return
else
if let urlContent = data
do
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
if let ip = jsonData["ip"]
finalData = ip as? String
completionHandler(finalData, nil)
return
catch
print("EMPTY")
task.resume()
return task
然后我在 viewDidLoad 中调用它
getJason(url) (ipAddress, error) -> Void in
if error != nil
print(error)
else
if let ip = ipAddress //To get rid of optional
self.ipLabelDisplay.text = "Your Ip Address is: \(ip)"
【讨论】:
以上是关于Swift:如何在异步 urlsession 函数中返回一个值?的主要内容,如果未能解决你的问题,请参考以下文章
如何在从 Swift 3 中的函数返回结果之前等待 URLSession 完成
从 URLSession 完成处理函数 Swift 3 返回字符串