如何为带回调的Swift函数封装async-await包装器并适配现有代码
问题核心
你的代码无法正常工作有两个关键原因:
- 异步回调的
completion闭包未声明为可抛出,你在其中执行throw error完全无效,且异步回调的错误无法传递到外部同步函数的调用层级。 - 封装的
save函数是同步返回Bool,但内部调用的是异步操作,这个返回值根本无法反映异步操作的最终状态。
两种可行解决方案
根据你需要获取操作成功/失败状态及错误信息的需求,提供两种适配方式:
方案一:用Result回调传递结果(适配现有回调代码)
如果暂时不想引入async-await,可以给封装函数添加一个带Result类型的回调,将异步操作的结果传递出去:
先补全必要的类型别名(如果项目中未定义):
typealias CountHandler = (Int) -> Void typealias ProgressHandler = (String) -> Void typealias SaveCompletion = (Result<Void, Error>) -> Void
修改后的封装函数:
private func save(records: [CKRecord], handler: ProgressHandler?, completion: @escaping SaveCompletion) { save(records: records, recordIDsToDelete: []) { count in handler?("Saving\n\(count)\nrecords.") } completion: { _, _, _, error in if let err = error { completion(.failure(err)) } else { completion(.success(())) } } }
调用示例:
save(records: yourRecordList, handler: { progressText in // 此处可更新进度UI或打印日志 print(progressText) }) { result in switch result { case .success: print("保存成功") case .failure(let error): print("保存失败:\(error.localizedDescription)") } }
方案二:提前适配async-await(推荐,契合你未来的重写计划)
直接将封装函数改为async-await形式,既能满足当前接入需求,也能为未来的重写工作无缝衔接:
private func save(records: [CKRecord], handler: ProgressHandler?) async throws { try await withCheckedThrowingContinuation { continuation in save(records: records, recordIDsToDelete: []) { count in handler?("Saving\n\(count)\nrecords.") } completion: { _, _, _, error in if let err = error { continuation.resume(throwing: err) } else { continuation.resume() } } } }
调用示例(需在async上下文执行,用Task包裹):
Task { do { try await save(records: yourRecordList) { progressText in print(progressText) } print("保存成功") } catch { print("保存失败:\(error.localizedDescription)") } }
方案选择建议
- 若现有代码完全基于回调风格,不想改动调用逻辑,选择方案一。
- 若想提前适配Swift现代异步编程规范,且未来计划重写为async-await,优先选择方案二,一步到位。
内容的提问来源于stack exchange,提问作者kunass2
相关产品推荐
相关产品推荐

