ios Swift:异步回调
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25055313/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift: Asynchronous callback
提问by stoeffn
How do I make asynchronous callbacks in swift? I'm writing a little Framework for my app because it's supposed to run on both, iOS und OS X. So I put the main code that is not device-specific into this framework that also handles requests to my online api. And obviously I also want the app's GUI and therefore my ViewControllers to react as soon as a api request has finished. In Objective-C I've done this by saving the view containing the function that had to be called in an id variable and the function itself in a selector variable. Then I invoked the function using the following code:
如何快速进行异步回调?我正在为我的应用程序编写一个小框架,因为它应该在 iOS 和 OS X 上运行。所以我将不是设备特定的主要代码放入这个框架中,该框架也处理对我的在线 api 的请求。显然,我还希望应用程序的 GUI 以及我的 ViewControllers 在 api 请求完成后立即做出反应。在 Objective-C 中,我通过保存包含必须在 id 变量中调用的函数和函数本身在选择器变量中的视图来完成此操作。然后我使用以下代码调用了该函数:
SEL selector = callbackMethod;
((void (*)(id, SEL))[callbackViewController methodForSelector:selector])(callbackViewController, selector);
How can I accomplish this in swift? Or is there a better way of doing this?
我怎样才能快速完成这个?或者有没有更好的方法来做到这一点?
I really appreciate all your help!
我真的很感谢你的帮助!
回答by user871177
I've shared the pattern that I use for this scenario in the following gist: https://gist.github.com/szehnder/84b0bd6f45a7f3f99306
我在以下要点中分享了我用于此场景的模式:https: //gist.github.com/szehnder/84b0bd6f45a7f3f99306
Basically, I create a singleton DataProvider.swift that setups an AFNetworking client. Then the View Controllers call methods on that DataProvider, each of which is terminated by a closure that I've defined as a typealias called ServiceResponse. This closure returns either a dictionary or an error.
基本上,我创建了一个单例 DataProvider.swift 来设置一个 AFNetworking 客户端。然后视图控制器调用该 DataProvider 上的方法,每个方法都由我定义为称为 ServiceResponse 的类型别名的闭包终止。这个闭包返回一个字典或一个错误。
It allows you to very cleanly (imo) call for an async data action from the VC's with a very clear indication of what you want performed when that async response returns.
它允许您非常干净地 (imo) 从 VC 调用异步数据操作,并非常清楚地指示当该异步响应返回时您想要执行的操作。
DataProvider.swift
数据提供者.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)
})
}
}
MyViewController.swift
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:"[email protected]", 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!")
}
}
}
}
回答by tikhop
I'd like to recommend use a block or closure callback instead of using NSThread and selectors.
我想推荐使用块或闭包回调而不是使用 NSThread 和选择器。
For example, in my API I have follow method:
例如,在我的 API 中,我有以下方法:
Swift:
迅速:
Below you will find an updated implementation.
您将在下面找到更新的实现。
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()
}
Objective-C:
目标-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];
}
Then, just make a call to api from view controller:
然后,只需从视图控制器调用 api:
Objc:
[api usersWithSucces:^(id result)
{
//Success callback
} failure:^(NSError *error)
{
//Failure callback
}];
Swift:
api.getUsers({(result: AnyObject?, error: NSError?) -> Int in
// callback here
})
UPDATE:
更新:
Meanwhile, I see that the question and answers are still being useful and interested. Well, here is an updated version of swift implementation using generic enum as a result object:
同时,我看到问题和答案仍然有用和感兴趣。好吧,这是使用通用枚举作为结果对象的 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
}
}
回答by Max_Power89
I've just made this little example: Swift: Async callback block pattern example
我刚刚做了这个小例子: Swift: Async callback block pattern example
Basically there is ClassA:
基本上有ClassA:
//ClassA it's the owner of the callback, he will trigger the callback when it's the time
class ClassA {
//The property of that will be associated to the ClassB callback
var callbackBlock : ((error : NSError?, message : String?, adress : String? ) -> Void)?
init() {
//Do Your staff
}
//Define your function with the clousure as a parameter
func yourFunctionWithCallback(#functionCallbackParameter : (error : NSError?,message : String?, adress : String?) -> ()) {
//Set the calback with the calback in the function parameter
self.callbackBlock = functionCallbackParameter
}
//Later On..
func callbackTrigger() {
self.callbackBlock?(error: nil,message: "Hello callback", adress: "I don't know")
}
}
And ClassB:
和 B 类:
//ClassB it's the callback reciver the callback
class ClassB {
@IBAction func testCallbackFunction(sender: UIButton) {
let classA = ClassA()
classA.yourFunctionWithCallback { (error, message, adress) -> () in
//Do your stuff
}
}
}
ClassA:it's the owns a property witch is the callbackBlock. ClassB will set this property by Call the yourFunctionWithCallback function. Later on then ClassA it's ready, will trigger the callback by calling the callBackBlock inside the callbackTrigger function.
ClassA:它拥有一个属性女巫是回调块。ClassB 将通过调用 yourFunctionWithCallback 函数来设置此属性。稍后 ClassA 准备就绪,将通过调用 callbackTrigger 函数内的 callBackBlock 来触发回调。
ClassB:will call the ClassA method to set the callback block and wait until the block has been triggered.
ClassB:将调用ClassA方法设置回调块并等待块被触发。
回答by Duyen-Hoa
Can NSThread help you? :
NSThread 能帮到你吗?:
NSThread.detachNewThreadSelector(<#selector: Selector#>, toTarget: <#AnyObject?#>, withObject: <#AnyObject?#>)