等待异步请求结果

Nik*_*vic 7 asynchronous ios swift alamofire

我想以某种方式异步验证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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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?
}
Run Code Online (Sandbox Code Playgroud)

使用信号量:

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
}
Run Code Online (Sandbox Code Playgroud)

编辑:

在Josip B.的建议之后,我在那时添加了信号量信号,但它仍然不起作用

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

ABPadLockScreen 引脚验证在a中的主线程上完成 ViewController

已解决的编辑:

感谢大家对我和我的耐心,不太好,对swift和iOS的了解.这里有很多好的答案,最后我认为最简单的解决方案.我听了Losiowaty的建议; 当我从服务器获得响应时,实现了一个微调器并手动解除了锁定屏幕.我使用过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
}
Run Code Online (Sandbox Code Playgroud)

Los*_*aty 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
}
Run Code Online (Sandbox Code Playgroud)

通过这种方法,您的用户将知道正在发生某些事情(例如,您可以将旋转器用作SVProgressHUD嵌入式解决方案)并且应用程序没有挂起。从用户体验角度来看,这一点非常重要,因为连接较差的用户可能会因为认为应用程序挂起并关闭它而感到沮丧。
但存在一个潜在的问题 - 如果当您从委托方法返回时挂锁屏幕显示某种“错误的 pin”消息false,则用户可能会看到它,从而造成一些混乱。现在可以通过制作/定位旋转器以模糊消息来解决这个问题,尽管这是一个非常粗糙且不优雅的解决方案。另一方面,也许它可以进行足够的自定义,以便不显示任何消息,并且您可以在服务器端验证后显示您自己的警报。

让我知道你对此有何看法!