Swift5 Network layer网络层Result闭包取值异常问题排查
网络层闭包取值问题修复方案
1. NetworkService 层已定位错误
- 仅POST方法会触发网络请求:GET分支配置完请求头后未创建、启动dataTask,所有GET请求不会实际发送
- 请求头键值赋值顺序颠倒:
addValue(_:forHTTPHeaderField:)第一个参数是头字段值,第二个参数是头字段名,原代码将key和value传反,所有自定义请求头不生效 - 强制解包崩溃风险:POST分支直接强制解包
parameters!,传入空参数时会直接触发运行时崩溃 - 异常分支无回调:URL初始化失败、请求头为空时直接return,未调用回调闭包传递错误,上层会永久等待响应
- 缺失HTTP状态码校验:拿到响应后未判断状态码是否为2xx区间,4xx/5xx服务端错误会被误判为请求成功
修复后的NetworkService代码:
// 补全错误枚举定义 enum ApiError: Error { case invalidURL case missingHeaders case recieveNilBody case invalidResponse case statusCodeError(Int) } // 确保APIMethod枚举的description返回GET/POST大写字符串 enum APIMethod { case GET, POST var description: String { switch self { case .GET: return "GET" case .POST: return "POST" } } } class NetworkService: Networking { func request( getURL: String, parameters: Dictionary<String, String>?, httpMethod: APIMethod, header: Dictionary<String, String>?, foregroundAPICall: Bool, debug: Bool, returnData: @escaping (_ result: Result<Data, Error>) -> Void ) { // URL初始化失败直接回调错误 guard let url = URL(string: getURL) else { returnData(.failure(ApiError.invalidURL)) return } var request = URLRequest(url: url) request.httpMethod = httpMethod.description // 统一处理请求头,GET/POST不需要重复编写逻辑 guard let headers = header else { returnData(.failure(ApiError.missingHeaders)) return } for (field, value) in headers { request.addValue(value, forHTTPHeaderField: field) } // 区分HTTP方法处理参数 switch httpMethod { case .GET: // GET参数可自行实现URLQuery拼接逻辑 break case .POST: request.setValue("application/json", forHTTPHeaderField: "Content-Type") // 判空后再序列化,避免强解崩溃 if let parameters = parameters { request.httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) } break } // 统一创建网络任务,GET/POST都能触发 let task = URLSession.shared.dataTask(with: request) { data, response, error in // 处理底层网络错误 if let error = error { returnData(.failure(error)) return } // 校验响应类型 guard let httpResponse = response as? HTTPURLResponse else { returnData(.failure(ApiError.invalidResponse)) return } // 校验状态码 guard (200...299).contains(httpResponse.statusCode) else { returnData(.failure(ApiError.statusCodeError(httpResponse.statusCode))) return } // 校验返回数据 guard let data = data else { returnData(.failure(ApiError.recieveNilBody)) return } if debug { print("接口原始返回:\(String(data: data, encoding: .utf8) ?? "")") } returnData(.success(data)) } task.resume() } }
2. DataFetcher 层闭包取值逻辑修复
原代码存在3个问题:
- 泛型约束缺失:实现类的
fetchGenericJsonData未声明T: Codable,无法执行JSON解码 - 闭包多余throws标记:异步回调闭包的错误无法被上层do-catch捕获,不需要加throws
- 未对下层返回的
Result做分支解析,无法拿到实际Data做模型解码
修复后的核心逻辑是通过switch解包Result类型,分别处理成功、失败分支:
protocol DataFetcher { func fetchGenericJsonData<T: Codable>( urlString: String, parameters: Dictionary<String, String>?, httpMethod: APIMethod, foregroundAPICall: Bool, header: Dictionary<String, String>?, debug: Bool, returnData: @escaping (_ result: Result<[T], Error>) -> Void ) } public class NetworkDataFetcher: DataFetcher { var networking: Networking init(networking: Networking = NetworkService()) { self.networking = networking } func fetchGenericJsonData<T: Codable>( urlString: String, parameters: Dictionary<String, String>?, httpMethod: APIMethod, foregroundAPICall: Bool, header: Dictionary<String, String>?, debug: Bool, returnData: @escaping (_ result: Result<[T], Error>) -> Void ) { networking.request( getURL: urlString, parameters: parameters, httpMethod: httpMethod, header: header, foregroundAPICall: foregroundAPICall, debug: debug ) { networkResult in // 闭包取值核心:switch解Result switch networkResult { case .success(let data): // 拿到原始Data后做JSON解码 do { let decoder = JSONDecoder() // 解码类型根据接口实际返回调整,单对象就改T.self,数组就用[T].self let decodedModel = try decoder.decode([T].self, from: data) returnData(.success(decodedModel)) } catch { // 解码失败透传错误 returnData(.failure(error)) } case .failure(let error): // 网络层错误直接透传 returnData(.failure(error)) } } } }
3. 上层调用示例
// 定义业务模型,遵守Codable struct User: Codable { let id: Int let name: String } // 发起请求 let fetcher = NetworkDataFetcher() fetcher.fetchGenericJsonData( urlString: "https://your-domain.com/api/users", parameters: nil, httpMethod: .GET, foregroundAPICall: true, header: ["Authorization": "Bearer your_token"], debug: true ) { result in // 上层同样用switch解包拿到最终结果 switch result { case .success(let userList): print("请求成功,用户列表:\(userList)") // UI操作必须切回主线程 DispatchQueue.main.async { // 刷新列表、赋值等业务逻辑 } case .failure(let error): print("请求失败:\(error.localizedDescription)") // 展示错误提示 } }
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

