如何告诉主线程 URLSessionDataTask 已经完成

Dav*_*gal 5 multithreading nsurlsession swift nsurlsessiondatatask

使用 Swift 4,我有以下代码尝试向 REST API 发出 POST 请求:

spinner.startAnimation(self)
btnOk.isEnabled = false
btnCancel.isEnabled = false

attemptPost()

spinner.stopAnimation(self)
btnOk.isEnabled = true
btnCancel.isEnabled = true
Run Code Online (Sandbox Code Playgroud)

执行此操作的函数(Constants并且Request是我创建的用于创建请求对象并保存常用数据的类):

func attemptPost() {
    let url = Constants.SERVICE_URL + "account/post"
    let body: [String : Any] =
        ["firstName": txtFirstName.stringValue,
        "lastName": txtLastName.stringValue,
        "email": txtEmail.stringValue,
        "password": txtPassword.stringValue];
    let req = Request.create(urlExtension: url, httpVerb: Constants.HTTP_POST, jsonBody: body)
    let task = URLSession.shared.dataTask(with: req) { data, response, err in
        guard let data = data, err == nil else {
            // error
            return
        }

        if let resp = try? JSONSerialization.jsonObject(with: data) {
            // success
        }
    }

    task.resume()
}
Run Code Online (Sandbox Code Playgroud)

由于执行此操作的任务异步运行,因此一旦调用attemptPost()返回,我就无法按顺序更新 UI 。而且由于 UI 组件在主线程上,我无法直接从发出请求的任务中更新组件。

在 C# 中,它的工作方式相同;有一个BackgroundWorker类,您可以在其中安全地更新 UI 组件以避免“跨线程操作无效”错误。

我试图找到一个或多或少完成相同事情的示例,其中建立“等待”状态,任务运行,并在任务完成后,通知主线程任务已完成,以便等待状态可以改变。

但是我仍然无法理解这一切是如何在 Swift 中结合在一起的。我环顾四周,看到了有关从内部调用的处理程序的信息URLSessionDataTask以及有关 GCD 的内容,但我仍然无法将这些点联系起来。

由于URLSessionDataTask任务一开始是异步的,因此 GCD 是否与此相关?

任何帮助表示赞赏。

And*_*ini 4

如果我理解正确的话你可以尝试这个解决方案:

spinner.startAnimation(self)
btnOk.isEnabled = false
btnCancel.isEnabled = false

attemptPost { (success) in
    DispatchQueue.main.async {
        spinner.stopAnimation(self)
        btnOk.isEnabled = true
        btnCancel.isEnabled = true
    }

   // UI wise, eventually you can do something with 'success'
}

func attemptPost(_ completion:@escaping (Bool)->())
    let url = Constants.SERVICE_URL + "account/post"
    let body: [String : Any] =
        ["firstName": txtFirstName.stringValue,
         "lastName": txtLastName.stringValue,
         "email": txtEmail.stringValue,
         "password": txtPassword.stringValue];
    let req = Request.create(urlExtension: url, httpVerb: Constants.HTTP_POST, jsonBody: body)
    let task = URLSession.shared.dataTask(with: req) { data, response, err in
        guard let data = data, err == nil else {
            completion(false)
            return
        }

        if let resp = try? JSONSerialization.jsonObject(with: data) {
            completion(true)
        }
    }
    task.resume()
}
Run Code Online (Sandbox Code Playgroud)

所以这个想法是从一个块执行,attemptPost该块将异步运行到你的 UI 内容的主线程中