如何告诉主线程 URLSessionDataTask 已经完成
How to tell the main thread that a URLSessionDataTask has finished
使用 Swift 4,我有这段代码尝试 POST 请求 REST API:
spinner.startAnimation(self)
btnOk.isEnabled = false
btnCancel.isEnabled = false
attemptPost()
spinner.stopAnimation(self)
btnOk.isEnabled = true
btnCancel.isEnabled = true
执行此操作的函数(Constants
和 Request
是我创建的用于创建请求对象并保存常用数据的 classes):
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()
}
由于执行此操作的任务异步运行,因此在调用 attemptPost()
returns 后,我无法按顺序更新 UI。由于 UI 组件在主线程上,我无法直接从发出请求的任务更新组件。
在 C# 中,它的工作方式相同;有一个 BackgroundWorker
class,您可以在其中安全地更新 UI 组件以避免 "Cross-thread operation not valid" 错误。
我试图找到一个完成或多或少相同事情的示例,其中建立了 "wait" 状态,任务运行,并在任务完成时通知主线程任务已完成,以便可以更改等待状态。
但我仍然无法理解这一切是如何在 Swift 中组合在一起的。我环顾四周,看到了有关从 URLSessionDataTask
中调用的处理程序的信息以及有关 GCD 的内容,但我仍然无法将这些点联系起来。
由于 URLSessionDataTask
任务从一开始就是异步的,因此 GCD 在这里是否相关?
感谢任何帮助。
如果我理解正确,您可以试试这个解决方案:
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()
}
所以这个想法是从 attemptPost
一个块执行的,该块将 运行 异步地进入主线程 UI stuff
使用 Swift 4,我有这段代码尝试 POST 请求 REST API:
spinner.startAnimation(self)
btnOk.isEnabled = false
btnCancel.isEnabled = false
attemptPost()
spinner.stopAnimation(self)
btnOk.isEnabled = true
btnCancel.isEnabled = true
执行此操作的函数(Constants
和 Request
是我创建的用于创建请求对象并保存常用数据的 classes):
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()
}
由于执行此操作的任务异步运行,因此在调用 attemptPost()
returns 后,我无法按顺序更新 UI。由于 UI 组件在主线程上,我无法直接从发出请求的任务更新组件。
在 C# 中,它的工作方式相同;有一个 BackgroundWorker
class,您可以在其中安全地更新 UI 组件以避免 "Cross-thread operation not valid" 错误。
我试图找到一个完成或多或少相同事情的示例,其中建立了 "wait" 状态,任务运行,并在任务完成时通知主线程任务已完成,以便可以更改等待状态。
但我仍然无法理解这一切是如何在 Swift 中组合在一起的。我环顾四周,看到了有关从 URLSessionDataTask
中调用的处理程序的信息以及有关 GCD 的内容,但我仍然无法将这些点联系起来。
由于 URLSessionDataTask
任务从一开始就是异步的,因此 GCD 在这里是否相关?
感谢任何帮助。
如果我理解正确,您可以试试这个解决方案:
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()
}
所以这个想法是从 attemptPost
一个块执行的,该块将 运行 异步地进入主线程 UI stuff