Swift 5.5适配async/await做API请求偶现NSURLErrorDomain -999错误
错误触发原因
- SwiftUI的
.task修饰符默认和所属视图的生命周期绑定,当视图在请求完成前被销毁/重绘时,修饰符管理的异步任务会自动被取消,Swift Concurrency体系下被取消的任务会自动终止正在执行的网络请求,抛出NSURLErrorDomain Code=-999错误。而你之前用的dataTask闭包实现不受视图生命周期管控,即使视图销毁也会继续执行完成,所以不会出现这类取消错误。 - 你在API单例的初始化方法中修改
URLSession.shared的配置属于无效操作,URLSession.shared的配置是只读属性,修改它不会生效,甚至可能引发未定义的网络行为,增加偶现问题的概率。 - 你当前的请求逻辑没有做任务取消的兼容处理,一旦任务被取消就直接触发
fatalError,没有容错空间。
解决方案
- 优化
.task修饰符的使用:给.task绑定固定的唯一标识,避免视图非必要重绘时反复取消重建任务,示例修改如下:
- 优化
.task(id: "init_api_request") { // 绑定固定id,只要id不变就不会取消现有任务 let api = API.singleton await api.makeAuthorization() await api.getTestConfiguration() }
如果业务允许请求不受视图生命周期影响,也可以将请求逻辑移到全局的业务管理类/StateObject中执行,避免和视图生命周期绑定。
- 替换全局共享URLSession,自定义独立的URLSession实例,正确配置超时参数:
在API类中新增自定义session属性,不要修改shared的配置:
- 替换全局共享URLSession,自定义独立的URLSession实例,正确配置超时参数:
// 替换原来直接修改URLSession.shared的逻辑 private let session: URLSession = { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 530.0 config.timeoutIntervalForResource = 560.0 return URLSession(configuration: config) }()
所有网络请求的地方都用这个自定义的session代替URLSession.shared。
- 添加取消错误的兼容处理,不要直接抛出
fatalError,可以根据业务需求选择忽略取消、自动重试或者提示用户:
- 添加取消错误的兼容处理,不要直接抛出
// 示例捕获取消错误的逻辑 do { let (data, response) = try await session.data(for: request) // 原有请求成功处理逻辑 } catch let error as URLError where error.code == .cancelled { print("请求被取消,可根据业务处理:比如静默忽略、触发重试") } catch { fatalError("请求失败: \(error)") }
内容的提问来源于stack exchange,提问作者kike
相关产品推荐
相关产品推荐

