英文:
Go back to previous page in Swift UI after Async func completion
问题
我有一个视图,其中包含以下代码:
@Environment(\.presentationMode) var presentationMode
...
Button {
Task {
await asyncFunction(data: data) {
self.presentationMode.wrappedValue.dismiss()
}
}
} label: {
Text("Save")
}
而我的异步函数如下所示:
func asyncFunction(data: Data, completion: @escaping () -> Void) async {
// 准备 URL
let url = URL(string: "api-url")
guard let requestUrl = url else { fatalError() }
// 将数据编码为 HTTP 请求的负载
let encodedData = try! JSONEncoder().encode(data)
// 执行 HTTP 请求
let (responseData, _) = try! await URLSession.shared.data(for: URLRequest(url: requestUrl, httpMethod: "POST", httpBody: encodedData))
// 将 HTTP 响应数据转换为字符串
if let responseData = Optional(responseData) {
if let dataString = String(data: responseData, encoding: .utf8) {
print("Response:\n \(dataString)")
}
}
// 调用完成处理程序
completion()
}
问题在于,一旦请求完成并且该调用dismiss()
的时候,应用程序会崩溃并显示以下错误:“Thread 18: EXC_BREAKPOINT (code=1, subcode=0x11022cd24)”。
看起来是与闭包有关的问题,但不确定具体原因是什么。
英文:
I have a view with the following code
@Environment(\.presentationMode) var presentationMode
...
Button {
Task {
await asyncFunction(data:data) {
self.presentationMode.wrappedValue.dismiss()
}
}
} label: {
Text("Save")
}
And my async function goes as follow:
func asyncFunction(data: Data, completion: @escaping () -> Void) async {
// Prepare URL
let url = URL(string: "api-url")
guard let requestUrl = url else { fatalError() }
// HTTP Request payload which will be sent in HTTP Request Body
let d = try! JSONEncoder().encode(data)
...
// Perform HTTP Request
let (responseData, _) = try! await URLSession.shared.data(for: request)
// Convert HTTP Response Data to a String
if let responseData = Optional(responseData) {
if let dataString = String(data: responseData, encoding: .utf8) {
print("Response:\n \(dataString)")
}
}
// Call the completion handler
completion()
}
The problem is that, once the the request is complete and it's time to call the dismiss()
, the app crashes with this error
Thread 18: EXC_BREAKPOINT (code=1, subcode=0x11022cd24)
.
Looks like a problem with the closure, but not sure to understand what is it?
答案1
得分: 1
Here is the translation of the provided code:
-
浮动的
Task
就像你在Button
中所使用的,传统上被认为是不良实践,因为你失去了对Task
本身的控制。 -
当采用
async await
时,完成处理程序已经成为过去,你应该永远(极少例外,我只知道一个例外)不要从async await
转到完成处理程序。
如果必须从完成处理程序转换到 async await
,则必须使用 CheckedContinuation
。
由于 async await
的行为就像是同步的,你可以完全删除完成处理程序。
Button {
Task {
await asyncFunction(data: data)
self.presentationMode.wrappedValue.dismiss()
}
} label: {
Text("保存")
}
func asyncFunction(data: Data) async {
// 准备 URL
let url = URL(string: "api-url")
guard let requestUrl = url else { fatalError() }
// HTTP 请求载荷将在 HTTP 请求正文中发送
let d = try! JSONEncoder().encode(data)
// 执行 HTTP 请求
let (responseData, _) = try! await URLSession.shared.data(for: requestUrl)
// 将 HTTP 响应数据转换为字符串
if let responseData = Optional(responseData) {
if let dataString = String(data: responseData, encoding: .utf8) {
print("响应:\n \(dataString)")
}
}
// 不要调用完成处理程序
}
现在要摆脱 Task
,你应该使用 .task
。
struct AsyncButton: View {
@Environment(\.dismiss) var dismiss
@State private var isProcessingFunction: Bool = false
var body: some View {
Button {
isProcessingFunction.toggle()
} label: {
Text("保存")
}.task(id: isProcessingFunction) {
guard isProcessingFunction else {
return
}
await asyncFunction(data: data)
dismiss()
isProcessingFunction = false
}
}
}
通过这种方式,你可以充分利用 async await
。
struct AsyncButton: View {
@Environment(\.dismiss) var dismiss
@State private var isProcessingFunction: Bool = false
var body: some View {
Button {
isProcessingFunction.toggle()
} label: {
Text("保存")
.overlay {
// 添加 ProgressView
if isProcessingFunction {
ProgressView()
}
}
}
.disabled(isProcessingFunction) // 可能禁用按钮以防止重复调用。或不允许用户取消调用。
.task(id: isProcessingFunction) {
guard isProcessingFunction else {
return
}
await asyncFunction(data: data)
dismiss()
isProcessingFunction = false
}
}
}
英文:
Several issues
-
Floating
Task
like what you have in theButton
are traditionally considered bad practice because you lose control of theTask
itself. -
Completion handlers are a thing of the past when adopting
async await
you should Never (rare exceptions I only know of one) go fromasync await
to a completion handler.
If you have to go from a completion handler to async await
you have to use a CheckedContinuation
.
Since async await
acts as if it is synchronous you can just remove the completion handler completely.
Button {
Task {
await asyncFunction(data:data)
self.presentationMode.wrappedValue.dismiss()
}
} label: {
Text("Save")
}
func asyncFunction(data: Data) async {
// Prepare URL
let url = URL(string: "api-url")
guard let requestUrl = url else { fatalError() }
// HTTP Request payload which will be sent in HTTP Request Body
let d = try! JSONEncoder().encode(data)
...
// Perform HTTP Request
let (responseData, _) = try! await URLSession.shared.data(for: request)
// Convert HTTP Response Data to a String
if let responseData = Optional(responseData) {
if let dataString = String(data: responseData, encoding: .utf8) {
print("Response:\n \(dataString)")
}
}
// NO Call the completion handler
}
Now to get rid of the Task
you should use .task
struct AsyncButton: View {
@Environment(\.dismiss) var dismiss
@State private var isProcessingFunction: Bool = false
var body: some View {
Button {
isProcessingFunction.toggle()
} label: {
Text("Save")
}.task(id: isProcessingFunction) {
guard isProcessingFunction else {
return
}
await asyncFunction(data:data)
dismiss()
isProcessingFunction = false
}
}
}
And with this you can take full advantage of async await
struct AsyncButton: View {
@Environment(\.dismiss) var dismiss
@State private var isProcessingFunction: Bool = false
var body: some View {
Button {
isProcessingFunction.toggle()
} label: {
Text("Save")
.overlay {
//Add a ProgressView
if isProcessingFunction {
ProgressView()
}
}
}
.disabled(isProcessingFunction) //Maybe disable button to prevent duplicate calls. Or not to allow the user to cancel the call.
.task(id: isProcessingFunction) {
guard isProcessingFunction else {
return
}
await asyncFunction(data:data)
dismiss()
isProcessingFunction = false
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论