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

如何用Swift Generics处理API请求的成功与错误响应?

你完全可以让这个函数同时处理两种响应结构,不需要强行给成功和错误类型加公共协议,两种常用的落地方式如下:

方案1:将错误结构作为自定义错误抛出(推荐,符合Swift throws语义)

你的原方案问题在于:调用时指定泛型T为Token,函数只会尝试将响应解码为Token,错误响应没有token字段自然解码失败,只会抛出系统默认的解码错误,不会走到TokenError的解码逻辑。
你可以修改函数逻辑,先尝试解码成功结构,失败时主动解码错误结构并作为自定义错误抛出:

  1. 首先让TokenError遵循Error协议,支持直接抛出:
extension TokenError: Error {}
  1. 修改请求函数,去掉泛型(你这个场景返回类型是固定的两种,不需要泛型),逻辑如下:
@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"
    ))
}
  1. 调用时直接在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 13:54:05