我想在SwiftUI中点击一个按钮,触发JSON编码操作。由于此操作耗时,因此我需要它是异步的。我已经尝试了两个解决方案,但它们都不起作用。一个主要问题是如何创建JSON编码的异步版本?
解决方案1)
解决方案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的主体中调用该函数:
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)
然而,用户界面被冻结,当encodeJSON完成后,它恢复正常,我可以与之交互。
我的问题是:如何创建一个JSONEncoder().encode(value: Data)的异步版本,并在SwiftUI按钮中调用它,而不会阻塞主线程(使用户界面冻结)?欢迎任何建议!
我尝试了两个解决方案。一个是从DispatchQueue.global().async {}创建一个异步版本并将其转换。另一个是直接将JSONEncoder().encode(value: Data)包装在异步函数中。但是,这两个解决方案都没有起作用。
我希望单击按钮后,相关的编码函数可以异步执行。
ObervableObject
并在那里完成繁重的工作。而不是使用DispatchQueue
和完成处理程序,使用async/await
。 - vadian@MainActor
和Task { @MainActor in …}
。@MainActor
标记异步方法,以便在主要执行者中高优先级地执行任务,而不是在后台优先级中执行。func encodeJSON() { Task { @MainActor in await viewModel.encodeJSON(_ value:)