iOS Swift/SwiftUI POST请求获取JSON类型报错解决方案
问题原因
你遇到的报错翻译为:无法将类型为'(Data?, URLResponse?, Error?) async -> Void'的函数传递给期望同步函数类型的参数。
触发原因很明确:URLSession.shared.dataTask(with: completionHandler:) 方法要求传入的completion回调是同步闭包,但你在闭包内部直接调用了await fetchData()这个异步方法,Swift会自动把当前闭包推断为async异步类型,和方法要求的参数类型不匹配,就会抛出这个错误。
修复方案
你写的getAppConfig本身已经标记为async函数,优先推荐用iOS 15及以上系统原生支持的async/await版本网络请求API,不需要写嵌套回调,逻辑更清晰,也不会出现闭包类型不匹配的问题。
方案1:使用现代async/await语法(推荐,适配iOS 15+)
直接替换整个方法实现即可:
func getAppConfig() async { guard let url = URL(string:"https://blasrv.com/appconfig.php") else { return } let body: [String: String] = ["userid": "420", "device": "ios"] let finalBody = try? JSONSerialization.data(withJSONObject: body) var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = finalBody request.setValue("application/json", forHTTPHeaderField: "Content-Type") do { // 调用async版本的网络请求方法,无需嵌套回调 let (data, response) = try await URLSession.shared.data(for: request) // 校验响应状态码,避免接口返回错误时仍正常解析 guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { print("接口响应异常") return } if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] { // 注意:如果接口返回的nickname是字符串而非字符串数组,请将下方类型判断改为 as? String if let nickname = json["nickname"] as? String { print(nickname) } } gotConfig = true await fetchData() } catch { print("请求/解析失败:\(error.localizedDescription)") } }
方案2:兼容低版本iOS的回调写法
如果需要适配iOS 15以下的系统,保留原有dataTask写法即可,只需要把闭包内的异步调用用Task包裹,避免闭包被推断为async类型:
// 只需要修改网络请求回调部分的代码 URLSession.shared.dataTask(with: request) { data, response, error in // 优先判断请求错误 guard error == nil, let data = data else { print("请求失败:\(error?.localizedDescription ?? "未知错误")") return } // 校验响应状态码 guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { print("响应状态码异常") return } do { // 原代码中let jsondata = Data(data)是多余操作,回调返回的data本身就是Data类型 if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] { if let nickname = json["nickname"] as? String { print(nickname) } } gotConfig = true // 用Task包裹异步调用,不改变闭包的同步属性 Task { await fetchData() } } catch { print("数据解析失败:\(error.localizedDescription)") } } .resume()
额外注意点
- 你原有代码里
let jsondata = Data(data)属于冗余操作,回调返回的data参数本身就是Data类型,不需要二次初始化 - 网络请求必须先判断
error是否为空、响应状态码是否为200,再做数据解析,否则很容易在请求失败时触发解析崩溃 - 注意核对接口返回字段的类型,绝大多数场景下
nickname是字符串类型,你原有代码里as? [String]是转成字符串数组,大概率拿不到返回值,需要根据接口实际返回调整类型判断
内容的提问来源于stack exchange,提问作者user10651308
相关产品推荐
相关产品推荐

