Swift:异步回调

sto*_*ffn 21 macos xcode asynchronous ios swift

如何在swift中进行异步回调?我正在为我的应用程序编写一个小框架,因为它应该在iOS和OS X上运行.所以我将非特定于设备的主代码放入此框架中,该框架也处理对我的在线api的请求.显然我也想要应用程序的GUI,因此我的ViewControllers会在api请求完成后立即做出反应.在Objective-C中,我通过保存包含必须在id变量中调用的函数的视图以及函数本身在选择器变量中来完成此操作.然后我使用以下代码调用该函数:

SEL selector = callbackMethod;
((void (*)(id, SEL))[callbackViewController methodForSelector:selector])(callbackViewController, selector);
Run Code Online (Sandbox Code Playgroud)

我怎样才能迅速实现这一目标?或者有更好的方法吗?

我非常感谢你的帮助!

use*_*177 30

我在以下要点中分享了我用于此场景的模式:https://gist.github.com/szehnder/84b0bd6f45a7f3f99306

基本上,我创建了一个单独的DataProvider.swift来设置AFNetworking客户端.然后View Controllers调用该DataProvider上的方法,每个方法都由一个闭包终止,我将其定义为一个名为ServiceResponse的类型.此闭包返回字典或错误.

它允许您非常干净地(imo)从VC调用异步数据操作,并清楚地指示异步响应返回时您想要执行的操作.

DataProvider.swift

typealias ServiceResponse = (NSDictionary?, NSError?) -> Void

class DataProvider: NSObject {

    var client:AFHTTPRequestOperationManager?
    let LOGIN_URL = "/api/v1/login"

    class var sharedInstance:DataProvider {
        struct Singleton {
            static let instance = DataProvider()
        }
        return Singleton.instance
    }

    func setupClientWithBaseURLString(urlString:String) {
        client = AFHTTPRequestOperationManager(baseURL: NSURL.URLWithString(urlString))
        client!.operationQueue = NSOperationQueue.mainQueue()
        client!.responseSerializer = AFJSONResponseSerializer()
        client!.requestSerializer = AFJSONRequestSerializer()
    }

    func loginWithEmailPassword(email:String, password:String, onCompletion: ServiceResponse) -> Void {
        self.client!.POST(LOGIN_URL, parameters: ["email":email, "password":password] , success: {(operation:AFHTTPRequestOperation!, responseObject:AnyObject!) -> Void in

            self.setupClientWithBaseURLString("http://somebaseurl.com")

            let responseDict = responseObject as NSDictionary
                // Note: This is where you would serialize the nsdictionary in the responseObject into one of your own model classes (or core data classes)
                onCompletion(responseDict, nil)
            }, failure: {(operation: AFHTTPRequestOperation!, error:NSError!) -> Void  in
                onCompletion(nil, error)
            })
    }
}
Run Code Online (Sandbox Code Playgroud)

MyViewController.swift

import UIKit

class MyViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override func viewWillAppear(animated: Bool)  {
        super.viewWillAppear(animated)
        DataProvider.sharedInstance.loginWithEmailPassword(email:"some@email.com", password:"somepassword") { (responseObject:NSDictionary?, error:NSError?) in

            if (error) {
                println("Error logging you in!")
            } else {
                println("Do something in the view controller in response to successful login!")
            }
        }
    }  
}
Run Code Online (Sandbox Code Playgroud)


tik*_*hop 24

我想建议使用块或闭包回调,而不是使用NSThread和选择器.

例如,在我的API中,我有以下方法:

迅速:

您将在下面找到更新的实施.

func getUsers(completion: (result: NSArray?, error: NSError?)->())
{
    var session = NSURLSession.sharedSession()
    var task = session.dataTaskWithRequest(request){
     (data, response, error) -> Void in
       if error != nil {
         completion(nil, error)
       } else {
         var result:NSArray = data to NSArray;
         completion(result, nil)
       }
    }
    task.resume()
}
Run Code Online (Sandbox Code Playgroud)

Objective-C的:

...
typedef void (^CBSuccessBlock)(id result);
typedef void (^CBFailureBlock)(NSError *error);
...

- (void)usersWithSucces:(CBSuccessBlock)success failure:(CBFailureBlock)failure
{
    NSURLSession *session = [NSURLSession sharedSession];
    [[session dataTaskWithURL:[NSURL URLWithString:url]
            completionHandler:^(NSData *data,
                                NSURLResponse *response,
                                NSError *error) {

                NSArray *users = //convert data to array

                if(error)
                    failure(error);
                else
                    success(users);
            }] resume];
}
Run Code Online (Sandbox Code Playgroud)

然后,只需从视图控制器调用api:

Objc:
[api usersWithSucces:^(id result)
{
   //Success callback
} failure:^(NSError *error)
{
   //Failure callback
}];

Swift:
api.getUsers({(result: AnyObject?, error: NSError?) -> Int in
    // callback here
})
Run Code Online (Sandbox Code Playgroud)

更新:

同时,我看到问题和答案仍然有用和感兴趣.好吧,这是使用泛型枚举作为结果对象的swift实现的更新版本:

//Generic enum that represents the result
enum AsyncResult<T>
{
    case Success(T)
    case Failure(NSError?)
}


class CustomUserObject
{

}

func getUsers(completion: (AsyncResult<[CustomUserObject]>)->())
{
    let request = NSURLRequest()
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(request){
        (data, response, error) -> Void in
        if let error = error
        {
            completion(AsyncResult.Failure(error))
        } else {
            let result: [CustomUserObject] = []//deserialization json data into array of [CustomUserObject]
            completion(AsyncResult.Success(result))
        }
    }
    task.resume()
}

//Usage:

getUsers { (result) in
    switch result
    {
    case .Success(let users):
        /* work with users*/
        break
    case .Failure(let error):
        /* present an error */
        break
    }
}
Run Code Online (Sandbox Code Playgroud)