[英]How to do asynchronous action with SwiftUI button
我想单击 SwiftUI 中的一个按钮,这将触发 JSON 编码操作。 这个动作很耗时,因此我需要它是异步的。 我已经尝试了两种解决方案,但它们不起作用。 一个主要问题是如何创建 json 编码的异步版本?
解决方案 1)
public func encodeJSON<T>(_ value: T, encoder: JSONEncoder, completionHandler: @escaping (Data?, Error?) -> Void) where T: Encodable {
DispatchQueue.global().async {
do {
let data = try encoder.encode(value)
DispatchQueue.main.async {
completionHandler(data, nil)
print("finish encode json")
}
} catch {
DispatchQueue.main.async {
completionHandler(nil, error)
print("fail encode json")
}
}
}
}
public func encodeJSON<T>(_ value: T, encoder: JSONEncoder) async throws -> Data where T: Encodable {
try await withUnsafeThrowingContinuation { continuation in
encodeJSON(value, encoder: encoder) { data, error in
if let error = error {
continuation.resume(throwing: error)
} else if let data = data {
continuation.resume(returning: data)
} else {
fatalError()
}
}
}
}
我在 SwiftUI 主体中调用 function:
Button {
let localDate = dailyUploadRecord.date!
let impactMed = UIImpactFeedbackGenerator(style: .medium)
impactMed.impactOccurred()
guard let file = UploadFileManager.shared.fetchedResults else { return }
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .millisecondsSince1970
Task {
isEncoding = true
let result = try await uploadManager.encodeJSON(file, encoder: encoder)
print(result)
isEncoding = false
}
} label: {
Text(“TEST")
.overlay {
if isEncoding {
ProgressView()
}
}
}
.disabled(isEncoding)
.buttonStyle(.bordered)
但是,它给了我运行时错误: Thread 6: EXC_BREAKPOINT (code=1, subcode=0x1b338b088)
然后,我尝试了第二种解决方案:
public func encodeJSON<T>(_ value: T, encoder: JSONEncoder) async throws -> Data where T: Encodable {
return try encoder.encode(value)
}
Button {
let localDate = dailyUploadRecord.date!
let impactMed = UIImpactFeedbackGenerator(style: .medium)
impactMed.impactOccurred()
guard let file = UploadFileManager.shared.fetchedResults else { return }
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .millisecondsSince1970
Task {
isEncoding = true
let result = try await encodeJSON(file, encoder: encoder)
print(result)
isEncoding = false
}
} label: {
Text(“TEST")
.overlay {
if isEncoding {
ProgressView()
}
}
}
.disabled(isEncoding)
.buttonStyle(.bordered)
但是,ui 被冻结,当 encodeJSON 完成后,它恢复正常,我可以与之交互。
我的问题是:如何创建异步版本的JSONEncoder().encode(value: Data) 并在SwiftUI 的Button 中调用它,而不阻塞主线程(使UI 冻结)? 欢迎任何建议!
我尝试了两种解决方案。 一种是从 DispatchQueue.global().async {} 创建一个异步版本并转换它。 另一种是直接将 JSONEncoder().encode(value: Data) 包装在异步 function 中。但是,这两种解决方案均无效。
我希望点击按钮,相关编码 function 可以异步执行。
第二种方式是正确的。 问题很可能是编码器抛出异常,当数据中的某些内容无法编码时会发生这种异常。 这意味着未到达isEncoding = false
行并且 UI 卡在编码 state 中。像这样修复它:
Task {
isEncoding = true
do {
let result = try await encodeJSON(file, encoder: encoder)
print(result)
}
catch {
print(error.localizedDescription)
}
isEncoding = false
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.