iOS开发报错:无法将异步Throws函数传入同步参数类型
错误解析与修复:Cannot pass function of type '(Bool) async throws -> Void' to parameter expecting synchronous function type
错误含义
这个错误的核心是闭包类型不匹配:你传入的闭包包含了async throws类型的代码(比如try await URLSession.shared.data(from: url)),导致闭包整体类型变成了(Bool) async throws -> Void,但downloadFileFromS3的completion参数要求的是同步、无抛出的(Bool)->Void闭包,两者类型不兼容,因此编译器报错。
解决思路
最简洁的解决方式是把传统的Completion Handler风格代码迁移到Swift的Async/Await异步模型,避免嵌套闭包带来的类型冲突,同时让代码逻辑更清晰。
关键修改步骤
- 将
downloadFileFromS3重构为异步抛出函数,替代原来的Completion Handler回调。 - 调整
downloadAppThemeJson的逻辑,用Async/Await链式调用替代嵌套闭包,避免在同步闭包中写入异步代码。 - 修复错误处理逻辑,把吞掉错误的
try?改为try,统一处理所有异常。
修改后的完整代码
重构后的S3下载函数
func downloadFileFromS3(client: String, fileName: String) async throws { let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let downloadFileURL = documentsUrl.appendingPathComponent(fileName) let s3BucketName = Constants.UIFileLocation.s3BucketName + client let s3DownloadKeyName = fileName let expression = AWSS3TransferUtilityDownloadExpression() // 用withCheckedThrowingContinuation将Completion Handler转为Async/Await风格 return try await withCheckedThrowingContinuation { continuation in let completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock = { task, location, data, error in DispatchQueue.main.async { do { // 处理下载数据为空的情况 guard let data = data else { let emptyError = NSError(domain: "S3DownloadError", code: -1, userInfo: [NSLocalizedDescriptionKey: "下载数据为空"]) continuation.resume(throwing: emptyError) return } // 写入本地文件,抛出错误让上层处理 try data.write(to: downloadFileURL) continuation.resume() } catch { continuation.resume(throwing: error) } } } let transferUtility = AWSS3TransferUtility.default() transferUtility.downloadData(fromBucket: s3BucketName, key: s3DownloadKeyName, expression: expression, completionHandler: completionHandler) .continueWith { task in // 处理S3传输任务本身的错误 if let error = task.error { continuation.resume(throwing: error) } return nil } } }
调整后的主题JSON下载与解析函数
func downloadAppThemeJson(completionHandler: @escaping(Bool) -> Void){ Task { do { // 1. 异步从S3下载文件到本地 try await downloadFileFromS3(client: self.selectedClient, fileName: Constants.ScreensUI.appInfo) // 2. 读取本地文件数据(直接用Data(contentsOf:),无需URLSession) let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileUrl = documentsUrl.appendingPathComponent(Constants.ScreensUI.appInfo) let data = try Data(contentsOf: fileUrl) // 3. 解析为自定义模型 let customModel = try JSONDecoder().decode(CustomModel.self, from: data) // 这里可以添加模型的后续处理逻辑,比如存储到全局变量或通知UI更新 print("模型解析成功: \(customModel)") completionHandler(true) } catch { // 统一处理所有错误:S3下载、文件读写、JSON解析 print("处理失败: \(error.localizedDescription)") completionHandler(false) } } }
额外说明
- 原代码中
downloadAppThemeJson里的fileName变量未定义,修改后替换为Constants.ScreensUI.appInfo,避免编译错误。 - 读取本地文件时使用
Data(contentsOf:)替代URLSession.shared.data(from:),因为本地文件无需网络请求,前者更高效。 - 所有错误都通过
throws抛出,统一在catch块中处理,避免了try?吞掉错误导致的调试困难。
内容的提问来源于stack exchange,提问作者Juno
相关产品推荐
相关产品推荐

