2016-07-31 108 views
7

我想以某種方式異步驗證ABPadLockScreen中的引腳,因爲引腳未保存在設備上。我使用Alamofire作爲http請求,並與PromiseKit一起承諾。我試圖使用AwaitKit但問題是我陷入僵局。等待異步請求結果

我也嘗試過使用semaphore,但結果是一樣的。由於我無法更改ABPadLock方法來容納像完成處理程序之類的東西,所以我需要一些解決方案,它是否阻塞主線程並不重要,只是它可以工作。

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銷驗證方法:

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 
} 

編輯:

從約瑟普B.我在加入旗語信號建議後,但它仍然無法正常工作

AirpharmService是包含靜態類屬性稱爲id,以及Alamofire請求方法。爲是與我和我的,不是那麼好,雨燕和iOS知識,使患者

感謝大家:

ABPadLockScreen引腳驗證在主線程中ViewController

解決的編輯完成。在這裏有很多很好的答案,最後,我只是在我看來是最簡單的解決方案。我聽了Losiowaty-s的建議;實現了一個微調器,並在我得到服務器響應時手動解鎖了鎖屏。我使用了SwiftSpinner。最終的解決方案看起來像這樣:

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 
} 
+1

您說「ABPadLockScreen引腳驗證在主線程上完成」,那麼您不應該在那裏等待。重新構建適合異步調用的驗證功能。 – OOPer

+0

這個函數'padLockScreenViewController'是abpadlockscreen協議的一部分,該函數由abpadlockscreen自動調用。你打算怎麼做?如果我問了一個愚蠢的問題,我真的很抱歉,但我一直在使用Swift幾天,所以我仍然不知道它是如何工作的。 –

+0

帶信號量的代碼不完整,添加dispatch_semaphore_signal(信號量);在loginResult = loginResultRaw之後發出信號量以繼續。 –

回答

3

很多人試圖幫助您使異步調用同步,這真是太好了。我個人同意@OOPer和他的意見,你應該重新設計你的代碼,尤其是在通過ABPadLockScreen代碼查看之後。看起來他們不支持異步引腳驗證,這是一種恥辱。另外,從他們的github回購看來,原來的作者至少暫時放棄了該項目。

我試圖解決您的問題是這樣的:

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作爲一個插入式解決方案)並且該應用沒有掛起。從根本上說,這是非常重要的,因爲連接不良的用戶可能會因爲認爲應用程序被吊死而關閉它而感到沮喪。
雖然存在潛在的問題 - 如果掛鎖屏幕在從代理方法返回false時顯示某種「錯誤的引腳」消息,則用戶可能會看到它,從而產生一些混淆。現在可以通過製作/定位微調器來解決這個問題,儘管這是一個非常粗糙和不合理的解決方案。另一方面,也許它可以進行定製,以便不會顯示任何消息,並且在服務器端驗證後您將顯示自己的警報。

讓我知道你對此有何想法!

0

我認爲信號量可以提供幫助。這裏是一個用法示例:

- (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。該方法getTasksWithCompletionHandlerNSURLSession一個方法,它將

異步調用,在一個會話中的所有數據,上傳和下載任務完成回調。

Semaphore_wait將確保任務已分配到適當的值。這樣你可以得到異步的請求結果。

+0

嗨,對不起,但它不起作用,我只是在一個無限循環... –

1

試試這個:

附加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) 
     } 
    } 
} 
1

根據我們交換的意見,聽起來像嘗試使用信號量時的無盡等待是因爲信號量信號永遠不會被髮送。讓我們試着簡化這個下降到需要測試的最小代碼:

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. 崩潰,因爲你是力展開幾個變量(例如baseUrl!loginResult.result!.id等,以及一個他們是零

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

  3. 返回false,如果你沒有得到有效的LoginResult

但理論上,它不應該發生死鎖。

3

......不管它是否阻塞主線程......但問題是我陷入了僵局。

一個問題可能是它擋住了main threaddispatch_semaphore_wait,所以Alamofire response沒有機會對main thread運行,你就死鎖。

對此的解決方案可以創建另一個隊列,其中分配了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) 
} 

用於測試的簡化版本。

//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 
} 
1

我試圖讓ABPadLockScreen支持異步引腳驗證。

我修改了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
作了演示項目請參閱swift演示示例。