如何用Swift Generics处理API请求的成功与错误响应?
你完全可以让这个函数同时处理两种响应结构,不需要强行给成功和错误类型加公共协议,两种常用的落地方式如下:
方案1:将错误结构作为自定义错误抛出(推荐,符合Swift throws语义)
你的原方案问题在于:调用时指定泛型T为Token,函数只会尝试将响应解码为Token,错误响应没有token字段自然解码失败,只会抛出系统默认的解码错误,不会走到TokenError的解码逻辑。
你可以修改函数逻辑,先尝试解码成功结构,失败时主动解码错误结构并作为自定义错误抛出:
- 首先让
TokenError遵循Error协议,支持直接抛出:
extension TokenError: Error {}
- 修改请求函数,去掉泛型(你这个场景返回类型是固定的两种,不需要泛型),逻辑如下:
@available(iOS 15.0.0, *) func requestToken(_ user: String, _ password: String) async throws -> Token { // 原有网络请求逻辑,拿到返回的Data let (data, _) = try await URLSession.shared.data(for: yourRequest) let decoder = JSONDecoder() // 优先尝试解码成功响应 if let token = try? decoder.decode(Token.self, from: data) { return token } // 解码失败尝试解码错误响应 if let errorResp = try? decoder.decode(TokenError.self, from: data) { throw errorResp } // 两种结构都匹配失败,抛出原生解码错误 throw DecodingError.dataCorrupted(.init( codingPath: [], debugDescription: "Response does not match Token or TokenError structure" )) }
- 调用时直接在catch分支捕获
TokenError即可读取属性:
do { let token = try await requestToken(login, password) // 处理正常业务逻辑 } catch let error as TokenError { // 直接读取业务错误属性 print("错误码:\(error.error.statusCode),错误信息:\(error.error.message)") } catch { // 处理其他错误(比如网络错误、解码失败等) }
方案2:用枚举封装两种响应结果
如果你不想用throws处理业务错误,可以定义一个联合枚举同时封装两种结果,自定义解码逻辑:
enum TokenResponse: Decodable { case success(Token) case failure(TokenError) init(from decoder: Decoder) throws { // 先尝试解码成功结构 if let token = try? Token(from: decoder) { self = .success(token) return } // 再尝试解码错误结构 if let error = try? TokenError(from: decoder) { self = .failure(error) return } // 都匹配失败抛出解码错误 throw DecodingError.dataCorrupted(.init( codingPath: decoder.codingPath, debugDescription: "Invalid response structure" )) } }
修改函数返回值为TokenResponse,调用时判断枚举分支即可:
@available(iOS 15.0.0, *) func requestToken(_ user: String, _ password: String) async throws -> TokenResponse { let (data, _) = try await URLSession.shared.data(for: yourRequest) return try JSONDecoder().decode(TokenResponse.self, from: data) } // 调用示例 let resp = try await requestToken(login, password) switch resp { case .success(let token): // 处理成功逻辑 case .failure(let error): // 处理错误逻辑 }
内容的提问来源于stack exchange,提问作者Chris Kobrzak
相关产品推荐
相关产品推荐

