Swift中泛型结构体ApiResponse的类型转换与statusCode获取问题
解决Swift泛型ApiResponse仅获取statusCode的类型推断问题
这个问题我之前也碰到过,泛型结构体只取部分字段的时候确实会卡在类型推断上,给你几个实用的解决方案,根据你的场景选就行:
方案1:通过协议提取公共字段
最省心的方法是定义一个仅包含statusCode的协议,让你的ApiResponse遵守它。这样不管泛型T是什么类型,都可以把实例转成这个协议类型来获取statusCode。
// 定义协议,只暴露需要的字段 protocol HasStatusCode: Codable { var statusCode: String? { get } } // 让原结构体遵守协议(不用改原有逻辑) struct ApiResponse<T: Codable>: Codable, HasStatusCode { let result: T? let statusCode: String? }
然后在你的网络回调里这么用:
NetworkLayer.requestObject(router: router) { (result: NetworkResult<T>) in switch result { case .success(let value): // 转成协议类型,不用关心T的具体类型 if let apiResponse = value as? HasStatusCode { let code = apiResponse.statusCode // 在这里处理statusCode } case .failure: break } }
这个方案几乎不用改动原有代码,兼容性拉满,所有ApiResponse<T>的实例都能适配。
方案2:用类型擦除的辅助结构体解码
如果你完全不想处理泛型T,可以写一个专门用来提取statusCode的非泛型结构体,自定义解码逻辑忽略result字段:
// 用来提取statusCode的辅助结构体 struct AnyApiResponse: Codable { let statusCode: String? enum CodingKeys: String, CodingKey { case result, statusCode } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 解码statusCode statusCode = try container.decodeIfPresent(String.self, forKey: .statusCode) // 忽略result字段,用AnyCodable兼容任意类型 _ = try container.decodeIfPresent(AnyCodable.self, forKey: .result) } } // 用来处理任意Codable类型的占位结构体 struct AnyCodable: Codable { init(from decoder: Decoder) throws {} func encode(to encoder: Encoder) throws {} }
使用的时候,把value转成Data再解码成AnyApiResponse:
NetworkLayer.requestObject(router: router) { (result: NetworkResult<T>) in switch result { case .success(let value): do { let data = try JSONEncoder().encode(value) let anyResponse = try JSONDecoder().decode(AnyApiResponse.self, from: data) let code = anyResponse.statusCode // 处理statusCode } catch { // 解码失败的容错处理 } case .failure: break } }
这个方案适合你完全不需要result字段,只想快速拿到statusCode的场景,缺点是多了一次编解码的开销。
方案3:修改网络层直接返回statusCode
如果NetworkLayer是你自己维护的,那从根源上解决问题更高效:在网络层内部解码ApiResponse,把statusCode单独提取出来放到NetworkResult里。
比如修改NetworkResult的定义:
enum NetworkResult<T> { case success(value: T, statusCode: String?) case failure(error: Error) }
然后在requestObject方法内部,解码ApiResponse<T>的时候把statusCode取出来,一起传给回调:
// 假设网络层内部是这样处理的 func requestObject<T: Codable>(router: Router, completion: @escaping (NetworkResult<T>) -> Void) { // 发起网络请求... if let data = responseData { do { let apiResponse = try JSONDecoder().decode(ApiResponse<T>.self, from: data) completion(.success(value: apiResponse.result!, statusCode: apiResponse.statusCode)) } catch { completion(.failure(error: error)) } } }
这样回调里就能直接拿到statusCode,完全不用做类型转换:
NetworkLayer.requestObject(router: router) { (result: NetworkResult<T>) in switch result { case .success(let value, let statusCode): // 直接使用statusCode case .failure: break } }
这个方案最优雅,避免了在业务代码里做类型判断和转换,但前提是你能修改网络层的代码。
内容的提问来源于stack exchange,提问作者Arslan Asim
相关产品推荐
相关产品推荐

