繁体   English   中英

如何用SwiftUI按钮做异步动作

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM