You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:36:15