ios - 等待异步请求结果

标签 ios swift asynchronous alamofire

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

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

我也尝试过使用semaphore,但结果是一样的。由于我无法更改 ABPadLock 方法来适应完成处理程序之类的东西,我需要一些解决方案,它是否阻塞主线程并不重要,只要它能工作即可。

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
}

编辑:

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

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

ABPadLockScreen pin 验证在 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
}

最佳答案

很高兴很多人试图帮助您使异步调用同步。我个人同意@OOPer 和他的意见,即您应该重新设计您的代码,尤其是在查看了 ABPadLockScreen 代码之后。他们似乎不支持异步 pin 验证,这是一种耻辱。此外,从他们的 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 作为嵌入式解决方案)并且应用程序没有挂起。这在用户体验方面非常重要,因为连接不畅的用户可能会因为认为应用挂起并关闭它而感到沮丧。
但是有一个潜在的问题 - 如果当您从委托(delegate)方法返回 false 时挂锁屏幕显示某种“错误的 pin”消息,则用户可能会看到它,从而造成一些困惑。现在可以通过制作/定位微调器来解决这个问题,使其模糊消息,尽管这是一个非常粗糙和不优雅的解决方案。另一方面,也许可以对其进行足够的自定义,以便不显示任何消息,并且您会在服务器端验证后显示自己的警报。

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

关于ios - 等待异步请求结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38684648/

相关文章:

swift - 使用 AVMetadataItem.value 快速获取媒体标题

swift - 从 [Any] 获取枚举原始值而不向下转换

c - MPI 异步/单边通信

iphone - objective c type cast an object based on an NSString 类名

ios - 在 ScrollView 上的 View 上添加约束显示异常

ios - 通过标签区分一个 UIButton

ios - 集中放置 Collection View 单元格

ios - 如何播放本地文件中的音频?

boost::asio 发送数据比通过 TCP 接收数据更快。或者如何禁用缓冲

java - Log4j2-日志在其他位置并行记录