在Swift函数中从asynchronous调用中返回数据

我在我的Swift项目中创build了一个工具类,它处理所有的REST请求和响应。 我已经构build了一个简单的REST API,所以我可以testing我的代码。 我创build了一个需要返回一个NSArray的类方法,但是因为API调用是asynchronous的,我需要从asynchronous调用中的方法返回。 问题是asynchronous返回void。 如果我在Node中这样做,我会使用JS承诺,但我不能找出一个在Swift中的解决scheme。

import Foundation class Bookshop { class func getGenres() -> NSArray { println("Hello inside getGenres") let urlPath = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list" println(urlPath) let url: NSURL = NSURL(string: urlPath) let session = NSURLSession.sharedSession() var resultsArray:NSArray! let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in println("Task completed") if(error) { println(error.localizedDescription) } var err: NSError? var options:NSJSONReadingOptions = NSJSONReadingOptions.MutableContainers var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: options, error: &err) as NSDictionary if(err != nil) { println("JSON Error \(err!.localizedDescription)") } //NSLog("jsonResults %@", jsonResult) let results: NSArray = jsonResult["genres"] as NSArray NSLog("jsonResults %@", results) resultsArray = results return resultsArray // error [anyObject] is not a subType of 'Void' }) task.resume() //return "Hello World!" // I want to return the NSArray... } } 

您可以传递callback,并在asynchronous调用中调用callback

就像是:

 class func getGenres(completionHandler: (genres: NSArray) -> ()) { ... let task = session.dataTaskWithURL(url) { data, response, error in ... resultsArray = results completionHandler(genres: resultsArray) } ... task.resume() } 

然后调用这个方法:

 override func viewDidLoad() { Bookshop.getGenres { genres in println("View Controller: \(genres)") } } 

Swiftz已经提供了Future,这是Promise的基本构build块。 未来是一个不容错过的承诺(这里的所有术语都是基于斯卡拉的解释, 其中一个承诺是一个单子 )。

https://github.com/maxpow4h/swiftz/blob/master/swiftz/Future.swift

希望最终可以扩展到一个完整的斯卡拉风格的承诺(我可以在某个时候自己写一个;我相信其他的PR将会受到欢迎; Future已经不是那么困难了)。

在您的具体情况下,我可能会创build一个Result<[Book]> (基于Alexandros Salazar版本的Result )。 那么你的方法签名将是:

 class func fetchGenres() -> Future<Result<[Book]>> { 

笔记

  • 我不build议在Swift中使用前缀函数。 它会打破某些与ObjC的互操作性。
  • 我build议将结果parsing为一个Book对象,然后将结果作为Future返回。 这个系统可能有多种失败的方法,如果你在将它们包装到Future之前检查所有这些东西,这将更为方便。 对于其他的Swift代码来说, [Book]要比交出一个NSArray要好得多。

另一个例子:

 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) } } 

@Alexey Globchastyy的Swift 3版的回答:

 class func getGenres(completionHandler: @escaping (genres: NSArray) -> ()) { ... let task = session.dataTask(with:url) { data, response, error in ... resultsArray = results completionHandler(genres: resultsArray) } ... task.resume() }