等待asynchronous请求结果

我想以某种方式asynchronousvalidation在ABPadLockScreen引脚,因为引脚没有保存在设备上。 我使用Alamofire和PromiseKit一起提供http请求来承诺。

我试图使用AwaitKit但问题是,我陷入僵局。

我也试图使用semaphore ,但结果是一样的。 因为我不能改变ABPadLock方法来容纳像完成处理程序这样的东西,所以我需要一些解决scheme,不pipe它是否阻塞主线程,只要它工作。

Alamofire请求方法:

 public func loginAsync(pinCode: String?, apiPath: String?) -> Promise<LoginResult>{ return Promise { fullfil, reject in let params = [ "Pin": pinCode! ] Alamofire.request(.POST, "\(baseUrl!)/\(apiPath!)", parameters: params).responseObject{(response: Response<LoginResult, NSError>) in let serverResponse = response.response if serverResponse!.statusCode != 200 { reject(NSError(domain: "http", code: serverResponse!.statusCode, userInfo: nil)) } if let loginResult = response.result.value { fullfil(loginResult) } } } } 

ABPadLockScreen引脚validation方法:

 public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool { let pinCode = pin! let defaults = NSUserDefaults.standardUserDefaults() let serverUrl = defaults.stringForKey(Util.serverUrlKey) let service = AirpharmService(baseUrl: serverUrl) service.loginAsync(pinCode, apiPath: "sw/airpharm/login").then { loginResult -> Void in if loginResult.code == HTTPStatusCode.OK { AirpharmService.id = loginResult.result!.id } } return false // how do i get the result of above async method here? } 

信号量:

 public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool { var loginResult: LoginResult? let defaults = NSUserDefaults.standardUserDefaults() let baseUrl = defaults.stringForKey(Util.serverUrlKey) let service = AirpharmService(baseUrl: baseUrl) let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0) service.loginAsync(pin, apiPath: "sw/airpharm/login").then { loginResultRaw -> Void in loginResult = loginResultRaw dispatch_semaphore_signal(semaphore)//after a suggestion from Josip B. } dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) return loginResult != nil // rudimentary check for now } 

编辑:

在Josip B.的build议之后,我添加了信号灯信号,但它仍然不起作用

AirpharmService是一个包含名为id的静态属性和Alamofire请求方法的类。

ABPadLockScreen引脚validation在ViewController主线程上完成

已解决编辑:

感谢大家对我和我的耐心,对iOS和Swift的不太了解。 这里有很多很好的答案,最后我只是跟着我,认为最简单的解决scheme。 我听了Losiowaty的build议, 实现了一个微调,当我得到服务器的响应时,手动解锁了锁屏。 我使用了SwiftSpinner 。 最终的解决scheme如下所示:

 public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool { let defaults = NSUserDefaults.standardUserDefaults() let baseUrl = defaults.stringForKey(Util.serverUrlKey) let service = AirpharmService(baseUrl: baseUrl) SwiftSpinner.show("Logging in. Please wait...") service.loginAsync(pin, apiPath: "sw/airpharm/login").then { loginResult -> Void in if loginResult.code == HTTPStatusCode.OK { SwiftSpinner.hide() AirpharmService.id = loginResult.result!.id self.unlockWasSuccessfulForPadLockScreenViewController(padLockScreenViewController) } else if loginResult.code == HTTPStatusCode.Unauthorized { let toast = JLToast.makeText("Invalid pin, please try again", duration: 5) toast.show() SwiftSpinner.hide() } else { let toast = JLToast.makeText("\(loginResult.code) sent from server. Please try again.", duration: 5) toast.show() SwiftSpinner.hide() } }.error { error in let toast = JLToast.makeText("\((error as NSError).code) sent from server. Please try again.", duration: 5) toast.show() SwiftSpinner.hide() } return false } 

很多人都试图帮助你使asynchronous调用同步。 就我个人而言,我同意@OOPer和他的意见,你应该重新devise你的代码,尤其是在查看ABPadLockScreen代码后。 看来他们不支持asynchronous引脚validation,这是一个耻辱。 另外,从他们的github回购看来,原来的作者至less暂时放弃了这个项目。

我试图解决你的问题是这样的:

 public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool { let pinCode = pin! let defaults = NSUserDefaults.standardUserDefaults() let serverUrl = defaults.stringForKey(Util.serverUrlKey) let service = AirpharmService(baseUrl: serverUrl) service.loginAsync(pinCode, apiPath: "sw/airpharm/login").then { loginResult -> Void in if loginResult.code == HTTPStatusCode.OK { AirpharmService.id = loginResult.result!.id self.handleLoginOk() } else { self.handleLoginFailed() } } // disable interaction on padlock screen // indicate to user that an async operation is going on, show a spinner return false // always return false here } func handleLoginOk() { // dismiss the ABPadlockScreenViewController manually } func handleLoginFailed() { // dismiss the spinner indicating the async operation // restore user interaction to padlock screen } 

通过这种方法,用户将知道正在发生的事情(微调,例如,可以使用SVProgressHUD作为插入式解决scheme),而且应用程序没有挂起。 这个问题非常重要,因为连接不好的用户可能会因为认为应用程序被吊死而感到沮丧。
有一个潜在的问题 – 如果挂锁屏幕显示某种“错误的引脚”的消息,当你从委托方法返回false时,它可能会对用户可见,造成一些混淆。 现在可以通过制作/定位旋转器来解决这个问题,尽pipe这是一个非常粗糙和不合理的解决scheme。 另一方面,也许它可以定制,以便没有消息显示,你会显示自己的警报后,服务器端validation。

让我知道你对此的看法!

如果它阻塞主线程并不重要…但问题是我陷入了僵局。

一个问题可能是它阻塞了dispatch_semaphore_wait main thread ,所以Alamofire response从来没有得到一个机会在main thread上运行,并且你是死锁。

解决这个问题的方法是创build另一个队列,在这个队列上Alamofire完成处理程序。

例如:

如果你提出这样的要求:

 Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts").validate().responseData() { response in print(response.result.value) } 

你可以修改这个调用来在你定义的queue调度完成处理程序,如下所示:

 let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT) let request = Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts", parameters: .None).validate() request.response(queue: queue, responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) { response in print(response.result.value) } 

testing的简化版本。

 //MARK: Lock Screen Delegate func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool { print("Validating Pin \(pin)") let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT) let semaphore = dispatch_semaphore_create(0) let request = Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts", parameters: .None).validate() request.response(queue: queue, responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments)) { response in print(response.result.value) //isPinValid = ??? dispatch_semaphore_signal(semaphore); } dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) return thePin == pin //return isPinValid } 

尝试这个:

添加dispatch_group:

 static let serviceGroup = dispatch_group_create(); 

然后调用该函数后,等待这个组:

 public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool { var loginResult: LoginResult? let defaults = NSUserDefaults.standardUserDefaults() let baseUrl = defaults.stringForKey(Util.serverUrlKey) let service = AirpharmService(baseUrl: baseUrl) let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0) service.loginAsync(pin, apiPath: "sw/airpharm/login").then { loginResultRaw -> Void in loginResult = loginResultRaw } dispatch_group_wait(yourClass.serviceGroup, DISPATCH_TIME_FOREVER); return loginResult != nil // rudimentary check for now } 

并且在函数返回答案后释放组:

 public func loginAsync(pinCode: String?, apiPath: String?) -> Promise<LoginResult>{ return Promise { fullfil, reject in let params = [ "Pin": pinCode! ] Alamofire.request(.POST, "\(baseUrl!)/\(apiPath!)", parameters: params).responseObject{(response: Response<LoginResult, NSError>) in let serverResponse = response.response if serverResponse!.statusCode != 200 { reject(NSError(domain: "http", code: serverResponse!.statusCode, userInfo: nil)) } if let loginResult = response.result.value { fullfil(loginResult) } dispatch_group_leave(yourClass.serviceGroup) } } } 

根据我们交换的意见,当您尝试使用信号量时听起来像无尽的等待是因为信号量信号永远不会被发送。 让我们试着简化到testing所需的最小代码:

 public func padLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!, validatePin pin: String!) -> Bool { var success = false let defaults = NSUserDefaults.standardUserDefaults() let baseUrl = defaults.stringForKey(Util.serverUrlKey) let semaphore: dispatch_semaphore_t = dispatch_semaphore_create(0) let params = ["Pin": pin] Alamofire.request(.POST, "\(baseUrl!)/sw/airpharm/login", parameters: params).responseObject { (response: Response<LoginResult, NSError>) in if let loginResult = response.result.value where loginResult.code == HTTPStatusCode.OK { AirpharmService.id = loginResult.result!.id success = true } dispatch_semaphore_signal(semaphore) } dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) return success } 

这应该是:

  1. 崩溃,因为你是强制loginResult.result!.id几个variables(例如loginResult.result!.id , loginResult.result!.id等,其中一个是零

  2. 如果你有一个有效的LoginResult返回true

  3. 如果没有得到有效的LoginResult则返回false

但从理论上讲,不应该是僵局。

我试图让ABPadLockScreen支持asynchronous引脚validation。

我修改了ABPadLockScreenViewController 。 添加了新的ABPadLockScreenViewControllerDelegate协议方法shouldValidatePinManuallyForPadLockScreenViewController:

 /** Call when pin validation is needed manually Call processUnlock method to validate manually if return true from this method */ - (BOOL)shouldValidatePinManuallyForPadLockScreenViewController:(ABPadLockScreenViewController *)padLockScreenViewController; 

增加了一个新的实例方法processUnlock

 - (void)processUnlock { if ([self isPinValid:self.currentPin]) { [self unlockScreen]; } else { [self processFailure]; } } 

修改了processPin方法

 - (void)processPin { if ([self.lockScreenDelegate respondsToSelector:@selector(shouldValidatePinManuallyForPadLockScreenViewController:)]) { if ([self.lockScreenDelegate shouldValidatePinManuallyForPadLockScreenViewController:self]) { return; } } [self processUnlock]; } 

现在在你的viewController中实现了shouldValidatePinManuallyForPadLockScreenViewController

 func shouldValidatePinManuallyForPadLockScreenViewController(padLockScreenViewController: ABPadLockScreenViewController!) -> Bool { print("Requesting server...") Alamofire.request(.GET, "https://jsonplaceholder.typicode.com/posts").validate().responseJSON() { response in //isPinValid = ??? print("Request complete") padLockScreenViewController.processUnlock() } return true } 

https://github.com/rishi420/ABPadLockScreen上制作了一个演示项&#x76EE;
请参阅swift演示示例。

我认为信号量可以帮助。 这是一个用法示例:

 - (NSArray *)tasksForKeyPath:(NSString *)keyPath { __block NSArray *tasks = nil; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) { tasks = dataTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) { tasks = uploadTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) { tasks = downloadTasks; } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) { tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"]; } dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); return tasks; } 

这是一个来自AFNetworking的function。 getTasksWithCompletionHandler方法是NSURLSession一个方法

在会话中asynchronous调用所有数据的完成callback,上载和下载任务。

Semaphore_wait将确保任务已被赋值为合适的值。 这样你可以得到asynchronous的请求结果。