等待异步请求结果 [英] Wait for asynchronous request result

查看:81
本文介绍了等待异步请求结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想以某种方式异步验证ABPadLockScreen中的引脚,因为引脚未保存在设备上。我将Alamofire与PromiseKit一起用于http请求,以实现承诺。



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



我也尝试过使用信号量,但是结果是一样的。由于我无法更改ABPadLock方法来容纳诸如完成处理程序之类的东西,因此我需要一些解决方案,因此它是否阻塞主线程也没关系,只是它可以正常工作。


Alamofire请求方法:




  public func loginAsync( pinCode:String?,apiPath:String?)-> Promise< LoginResult> {
return Promise {fullfil,在
中拒绝let params = [
Pin:pinCode!
]

Alamofire.request(.POST, \(baseUrl!)/ \(apiPath!),参数:params).responseObject {(response:Response< LoginResult, NSError>)在
中,如果serverResponse!.statusCode!= 200 {
reject(NSError(domain: http,代码:serverResponse !.),则让serverResponse = response.response

。 statusCode,userInfo:nil))
}

如果让loginResult = response.result.value {
fullfil(loginResult)
}
}
}
}




ABPadLockScreen引脚验证方法:




 公共功能padLockScreenViewController(padLockScreenViewController:ABPadLockScreenViewController !, validatePin pin:String!)-> Bool {
让pinCode = pin!
let defaults = NSUserDefaults.standardUserDefaults()
let serverUrl = defaults.stringForKey(Util.serverUrlKey)
let service = AirpharmService(baseUrl:serverUrl)

服务。 loginAsync(pinCode,apiPath: sw / airpharm / login)。然后{loginResult->如果loginResult.code == HTTPStatusCode.OK {
AirpharmService.id = loginResult.result!.id
}
}

返回false //我如何在这里获得上述异步方法的结果?
}




带有信号量:




 公共功能padLockScreenViewController(padLockScreenViewController:ABPadLockScreenViewController !, validatePin pin:String!)-> Bool {

var loginResult:LoginResult?

让默认值= NSUserDefaults.standardUserDefaults()

让baseUrl = defaults.stringForKey(Util.serverUrlKey)

让服务= AirpharmService(baseUrl: baseUrl)

让信号量:dispatch_semaphore_t = dispatch_semaphore_create(0)

service.loginAsync(pin,apiPath: sw / airpharm / login)。然后{登录结果原始->
中的void loginResult = loginResultRaw
dispatch_semaphore_signal(semaphore)//根据Josip B的建议。
}

dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER)

return loginResult!= nil //初步检查
}

编辑:


在Josip B提出建议之后,我在当时添加了信号灯信号,但它仍然不起作用


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



ABPadLockScreen 引脚验证是在 ViewController



已解决的编辑:



感谢大家的参与所以对我和我的耐心(不是很好)对Swift和iOS的了解。在这里有很多很好的答案,最后,我认为,最简单的解决方案就是采用了。我听了 Losiowaty -s的建议;当我从服务器得到响应时,实现了一个微调器并手动解除了锁定屏幕。我使用了 SwiftSpinner 。最终解决方案如下所示:

 公共功能padLockScreenViewController(padLockScreenViewController:ABPadLockScreenViewController !, validatePin pin:String!)->布尔{
let defaults = NSUserDefaults.standardUserDefaults()
let baseUrl = defaults.stringForKey(Util.serverUrlKey)

let service = AirpharmService(baseUrl:baseUrl)
SwiftSpinner.show(正在登录。请稍候...)
service.loginAsync(pin,apiPath: sw / airpharm / login)。然后{loginResult->如果loginResult.code == HTTPStatusCode.OK {
SwiftSpinner.hide()
AirpharmService.id = loginResult.result!.id
self.unlockWasSuccessfulForPadLockScreenViewController(padLockScreenViewController)
}否则,如果loginResult.code == HTTPStatusCode.Unauthorized {
let toast = JLToast.makeText(无效的密码,请重试,持续时间:5)
toast.show()
SwiftSpinner.hide()
}否则{
let toast = JLToast.makeText( \(loginResult.code)从服务器发送。请重试。,持续时间:5)
toast.show()
SwiftSpinner.hide()
}
} .error {
中的错误let toast = JLToast.makeText( \((错误为NSError) (代码。),请重试。,持续时间:5)
toast.show()
SwiftSpinner.hide()
}

返回false
}


解决方案

很多人试图帮助您使异步呼叫同步,这一点很棒。我个人同意@OOPer和他的意见,即应该重新设计代码,尤其是在阅读 ABPadLockScreen 代码之后。看来他们不支持异步管脚验证,这很遗憾。从他们的github回购看来,原作者至少暂时还放弃了该项目。



我会尝试解决您的问题,如下所示:

 公共功能padLockScreenViewController(padLockScreenViewController:ABPadLockScreenViewController !, validatePin pin:String!)-> Bool {
让pinCode = pin!
let defaults = NSUserDefaults.standardUserDefaults()
let serverUrl = defaults.stringForKey(Util.serverUrlKey)
let service = AirpharmService(baseUrl:serverUrl)

服务。 loginAsync(pinCode,apiPath: sw / airpharm / login)。然后{loginResult->如果loginResult.code == HTTPStatusCode.OK {
AirpharmService.id = loginResult.result!.id
self.handleLoginOk()
}否则{
self.handleLoginFailed()
}
}

//在挂锁屏幕上禁用交互
//向用户指示正在进行异步操作,显示微调器

return false //总是在此处返回false
}

func handleLoginOk(){
//手动关闭ABPadlockScreenViewController
}

func handleLoginFailed(){
//关闭微调器,指示异步操作
//将用户交互恢复到挂锁屏幕
}

通过这种方法,您的用户将知道发生了什么事情(微调器,例如,您可以使用 SVProgressHUD 作为嵌入式解决方案),并且该应用未挂起。在用户体验方面,这非常重要,因为连接不良的用户可能会因为挂断应用程序而感到沮丧,并关闭了该应用程序。

但是存在潜在的问题-如果挂锁屏幕显示某种错误的密码消息,当您从委托方法返回 false 时,用户可能会看到它,从而造成一些混乱。现在,可以通过制作/定位微调器来解决此问题,以使它掩盖信息,尽管这是一个非常粗糙且毫无意义的解决方案。另一方面,也许可以对其进行足够的自定义,以便不显示任何消息,并且在服务器端验证后您将显示自己的警报。



让我知道什么您会想到的!


I would like somehow to asynchronously validate the pin in ABPadLockScreen since pins are not saved on the device. I'm using Alamofire for http requests along with PromiseKit to have promises.

I have tried to use AwaitKit but the problem is that i get into a deadlock.

I have also tried to use semaphore as well, but the result is the same. Since i can't change the ABPadLock method to accommodate something like a completion handler i need some solution, it doesn't matter if it blocks the main thread, just that it works.

Alamofire request method:

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 pin validation method:

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?
}

With semaphore:

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
}

EDIT:

After a suggestion from Josip B. i added semaphore signal in then, but it still doesn't work

AirpharmService is a class that contains a static property called id, and the Alamofire request method.

ABPadLockScreen pin validation is done on main thread in a ViewController

SOLVED EDIT:

Thanks to everyone for being so patient with me and my, not so good, knowledge of swift and iOS. There are a lot of good answers here and in the end i just went with, in my opinion, simplest solution. I listened to Losiowaty-s suggestion; implemented a spinner and manually dismissed the lock screen when i get the response from the server. I've used a SwiftSpinner. The final solution looked like this:

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
}

解决方案

It's great that a lot of people tried to help you make your asynchronous call synchronous. Personally I agree with @OOPer and his comment that you should redesign your code, especially after looking through ABPadLockScreen code. It seems they don't support asynchronous pin verification, which is a shame. Also it seems from their github repo that the original author has abandoned the project, for the time being at least.

I'd attempt to solve your issue like this :

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
}

With this approach your users will know that something is going on (the spinner, you can use for example SVProgressHUD as a drop-in solution) and that the app didn't hang. It is quite important, ux-wise, as users with poor connection could get frustrated thinking the app hanged and close it.
There is a potential problem though - if the padlock screen shows some kind of "wrong pin" message when you return false from the delegate method, it could be visible to the user creating some confusion. Now this can be tackled by making/positioning the spinner so that it obscures the message, though this is a very crude and unelegant solution. On the other hand, maybe it can be customised enough so that no message gets shown, and you'd display your own alert after server side verification.

Let me know what you think about this!

这篇关于等待异步请求结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆