xcode 在 restful 回调 (swift) 上呈现 segue

xcode present segue on restful callback (swift)

我是自学成才的 Swift 用户,我尝试做一些简单的事情,但这让我很困惑。我有一个简单的注册表。提交项目注册后,我想通过 segue 将页面移动到 "how it works" 页面,但只有当我的 restful API returns 成功时。到目前为止,这是我所拥有的;也请随时给我一个更好的方法来做到这一点。欢迎所有批评。

let myUrl = NSURL(string:"http://www.example.com/scripts/Register.php")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"

let postString = "email=\(email)&password=\(pass)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)

let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
    data, response, error in

    if (error != nil) {
        println("Error: \(error)")
        return
    }

    var err: NSError?
    var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
    var showTutorial : Bool = false

    if let parseJSON = json {
        var returnValue = parseJSON["status"] as? String
        println("Status: \(returnValue)")

        var isUserRegistered: Bool = false
        if (returnValue == "Success") {
            showTutorial = true
        } else if (returnValue == "Error") {                    
            // handle error
        }
    }

    // if successful registration, show how it works page
    if (showTutorial) {
        self.performSegueWithIdentifier("howItWorksSegue", sender: self)
    }
}
task.resume()

我有一个名为 howItWorksSegue 的 segue 附加到此视图控制器,将转到 HowItWorksViewController。我从 Xcode:

收到这个错误

2015-10-12 21:22:43.261 ZiftDine[11396:2307755] Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /SourceCache/UIKit_Sim/UIKit-3347.44.2/Keyboard/UIKeyboardTaskQueue.m:374
2015-10-12 21:22:43.391 ZiftDine[11396:2307755] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'

任何用 UI 完成的事情都应该在主线程上完成,尝试像这样包装你的 performSegue 调用:

dispatch_async(dispatch_get_main_queue(),{
    self.performSegueWithIdentifier("howItWorksSegue", sender: self)
})

@Swinny89 给出了您问题的解决方案,但需要一些解释。

如果您阅读了 dataTaskWithRequest:completionHandler: 的描述,这是您正在使用的方法(尽管您的 Swift 代码使用尾随闭包语法删除 completionHandler 标签并放置闭包在括号外)它说:

completionHandler: The completion handler to call when the load request is complete. This handler is executed on the delegate queue.

然后如果你阅读 init 方法的描述 sessionWithConfiguration:delegate:delegateQueue: 它说:

queue: A queue for scheduling the delegate calls and completion handlers. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.

不同线程上的串行操作队列 运行。

因此,综合所有这些信息,这意味着您的完成闭包将在主线程以外的线程上执行。

iOS/Mac 开发的一个基本规则是您必须从主线程执行所有 UI 调用。如果通话改变了屏幕上的任何内容,则为 UI 通话。

您的代码正在从后台线程调用 performSegueWithIdentifier:。它会更改屏幕上显示的内容,因此它必须是 UI 调用。因此它需要在主线程上 运行。

GCD 函数 dispatch_async(),队列 dispatch_get_main_queue(),提交一个闭包到主调度队列 运行,运行 的队列主线程。

所以 Swinny 的解决方案解决了您的问题。

这里的要点:

任何时候你在闭包中 运行 宁代码,停下来思考:"Am I positive that this closure will always be run on the main thread?" 如果答案是否定的,将代码包含在对 dispatch_async(dispatch_get_main_queue() 的调用中,就像 Swinny 的回答。

@Duncan C 和@Swinny89 的回答很好。对于来自 Google 的任何人,Swift 3 中的语法略有变化:

DispatchQueue.main.async(execute: {
    self.performSegueWithIdentifier("howItWorksSegue", sender: self)
})