Ios 从Swift函数中的异步调用返回数据

Ios 从Swift函数中的异步调用返回数据,ios,rest,asynchronous,swift,Ios,Rest,Asynchronous,Swift,我在Swift项目中创建了一个实用程序类,用于处理所有REST请求和响应。我已经构建了一个简单的RESTAPI,所以我可以测试我的代码。我创建了一个需要返回NSArray的类方法,但是因为API调用是异步的,所以我需要从异步调用中的方法返回。问题是异步返回void。 如果我在Node中这样做,我会使用JS承诺,但我无法找到一个在Swift中工作的解决方案 import Foundation class Bookshop { class func getGenres() -> NS

我在Swift项目中创建了一个实用程序类,用于处理所有REST请求和响应。我已经构建了一个简单的RESTAPI,所以我可以测试我的代码。我创建了一个需要返回NSArray的类方法,但是因为API调用是异步的,所以我需要从异步调用中的方法返回。问题是异步返回void。 如果我在Node中这样做,我会使用JS承诺,但我无法找到一个在Swift中工作的解决方案

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

您可以传递回调,并在异步调用中调用回调

比如:

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

您可以传递回调,并在异步调用中调用回调

比如:

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

斯威夫茨已经提供了未来,这是承诺的基本组成部分。未来是一个不能失败的承诺(这里的所有术语都基于Scala的解释,)

希望最终能够扩展到一个完整的Scala风格的承诺(我可能会在某个时候自己编写;我相信其他PRs也会受到欢迎;未来已经存在,这并不难)

在您的特定情况下,我可能会创建一个
结果
(基于)。那么您的方法签名将是:

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

  • 我不建议在Swift中使用
    get
    作为函数前缀。它会破坏与ObjC的某些互操作性
  • 我建议在将结果作为
    未来
    返回之前,先解析一个
    书籍
    对象。这个系统有几种可能失败的方法,如果在将它们包装成
    未来
    之前检查所有这些内容,会更方便。进入
    [书籍]
    对于Swift代码的其余部分来说比交给NSArray要好得多

Swiftz已经提供了未来,这是承诺的基本组成部分。未来是一个不能失败的承诺(这里的所有条款都基于Scala的解释,)

希望最终能够扩展到一个完整的Scala风格的承诺(我可能会在某个时候自己编写;我相信其他PRs也会受到欢迎;未来已经存在,这并不难)

在您的特定情况下,我可能会创建一个
结果
(基于)。然后您的方法签名将是:

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

  • 我不建议在Swift中使用
    get
    作为函数前缀。它会破坏与ObjC的某些互操作性
  • 我建议在将结果作为
    未来
    返回之前,先解析一个
    书籍
    对象。这个系统有几种可能失败的方法,如果在将它们包装成
    未来
    之前检查所有这些内容,会更方便。进入
    [书籍]
    对于Swift代码的其余部分来说比交给NSArray要好得多

Swift 3版@Alexey Globchasty的答案:

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

@Alexey Globchasty答案的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()
}

Swift 4.0

对于异步请求-响应,您可以使用完成处理程序。请参阅下文,我已使用完成处理程序范例修改了解决方案

func getGenres(_ completion: @escaping (NSArray) -> ()) {

        let urlPath = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list"
        print(urlPath)

        guard let url = URL(string: urlPath) else { return }

        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            do {
                if let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
                    let results = jsonResult["genres"] as! NSArray
                    print(results)
                    completion(results)
                }
            } catch {
                //Catch Error here...
            }
        }
        task.resume()
    }
您可以按如下方式调用此函数:

getGenres { (array) in
    // Do operation with array
}

Swift 4.0

对于异步请求-响应,您可以使用完成处理程序。请参阅下文,我已使用完成处理程序范例修改了解决方案

func getGenres(_ completion: @escaping (NSArray) -> ()) {

        let urlPath = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list"
        print(urlPath)

        guard let url = URL(string: urlPath) else { return }

        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            do {
                if let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
                    let results = jsonResult["genres"] as! NSArray
                    print(results)
                    completion(results)
                }
            } catch {
                //Catch Error here...
            }
        }
        task.resume()
    }
您可以按如下方式调用此函数:

getGenres { (array) in
    // Do operation with array
}

我希望你还没有陷入这个问题,但简单的回答是,你不能在Swift中这样做

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

另一种方法是返回一个回调,在它准备好后立即提供您所需的数据。

我希望您还没有陷入这个问题,但简单的回答是,您不能在Swift中这样做

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

另一种方法是返回一个回调,该回调将在数据准备好后立即提供所需的数据。

基本模式是使用完成处理程序

例如,我们经常使用
结果

func fetchGenres(completion: @escaping (Result<[Genre], Error>) -> Void) {
    ...
    URLSession.shared.dataTask(with: request) { data, _, error in 
        if let error = error {
            DispatchQueue.main.async {
                completion(.failure(error))
            }
            return
        }

        // parse response here

        let results = ...
        DispatchQueue.main.async {
            completion(.success(results))
        }
    }.resume()
}
注意,上面我将完成处理程序分派回主队列以简化模型和UI更新。一些开发人员对此做法持异议,要么使用使用的任何队列
URLSession
,要么使用自己的队列(要求调用方自己手动同步结果)

但这并不重要,关键问题是使用完成处理程序来指定异步请求完成时要运行的代码块



注意,在上面,我不再使用
NSArray
(我们不再使用了)。我假设我们有一个
类型
类型,我们大概使用了
jsondeconder
,而不是
JSONSerialization
,来解码它。但是这个问题没有足够的关于底层JSON的信息来深入这里的细节,所以我省略了这一点,以避免混淆核心问题,使用闭包作为完成处理程序。

基本模式是使用完成处理程序闭包

例如,我们经常使用
结果

func fetchGenres(completion: @escaping (Result<[Genre], Error>) -> Void) {
    ...
    URLSession.shared.dataTask(with: request) { data, _, error in 
        if let error = error {
            DispatchQueue.main.async {
                completion(.failure(error))
            }
            return
        }

        // parse response here

        let results = ...
        DispatchQueue.main.async {
            completion(.success(results))
        }
    }.resume()
}
注意,上面我将完成处理程序分派回主队列以简化模型和UI更新。一些开发人员对此做法持异议,要么使用使用的任何队列
URLSession
,要么使用自己的队列(要求调用方自己手动同步结果)

但这并不重要,关键问题是使用完成处理程序来指定异步请求完成时要运行的代码块


注意,上面我不再使用
NSArray
(我们不再使用了)。我假设我们有一个
类型
,我们大概使用
JSONDecoder
,而不是
JSONSerialization
,来解码它。但是这个问题没有足够的inf