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

Swift 5.8中如何泛型处理API返回的多种Token<T>类型?

泛型处理Swift中Token<T>类型的问题

API核心定义

public struct Token<T> : Codable, Equatable, Hashable { }

public struct Application : Equatable, Hashable {
    public let token: ApplicationToken?
}

public struct ActivityCategory : Equatable, Hashable {
    public let token: ActivityCategoryToken?
}

public struct WebDomain : Equatable, Hashable {
    public let token: WebDomainToken?
}

public typealias ApplicationToken = Token<Application>
public typealias ActivityCategoryToken = Token<ActivityCategory>
public typealias WebDomainToken = Token<WebDomain>

// API返回的数据结构:
struct ActivitySelection {
    public var applicationTokens: Set<ApplicationToken>
    public var categoryTokens: Set<ActivityCategoryToken>
    public var webDomainTokens: Set<WebDomainToken>
}

遇到的编译错误

  • 使用带泛型参数的方法时,出现XCode错误:无法推断泛型参数'T'
  • 移除强制类型转换后,出现类型不匹配错误,例如无法将[ApplicationToken]赋值给[Token<T>]
  • 尝试返回[Token<Any>]时,同样出现类型不匹配错误

需求

如何泛型处理这三种不同的Token<T>类型?


解决方案

方法1:通过协议约束实现泛型访问

先给Token<T>的关联类型定义通用协议,让所有关联类型遵循该协议,以此实现统一的泛型处理:

// 定义通用协议,约束Token的关联类型
protocol Tokenable: Equatable, Hashable {}

// 让三个结构体遵循协议
extension Application: Tokenable {}
extension ActivityCategory: Tokenable {}
extension WebDomain: Tokenable {}

基于协议给ActivitySelection扩展泛型方法,根据传入的类型返回对应Token集合:

extension ActivitySelection {
    func tokens<T: Tokenable>(for type: T.Type) -> Set<Token<T>> {
        switch type {
        case is Application.Type:
            return applicationTokens as! Set<Token<T>>
        case is ActivityCategory.Type:
            return categoryTokens as! Set<Token<T>>
        case is WebDomain.Type:
            return webDomainTokens as! Set<Token<T>>
        default:
            return []
        }
    }
}

使用示例:

let selection = ActivitySelection(
    applicationTokens: [],
    categoryTokens: [],
    webDomainTokens: []
)

let appTokens = selection.tokens(for: Application.self) // 类型为Set<ApplicationToken>
let categoryTokens = selection.tokens(for: ActivityCategory.self) // 类型为Set<ActivityCategoryToken>

方法2:用枚举统一封装所有Token类型

定义枚举包裹不同类型的Token,实现统一管理和遍历:

enum AnyToken: Equatable, Hashable {
    case application(ApplicationToken)
    case category(ActivityCategoryToken)
    case webDomain(WebDomainToken)
    
    // 从Token<T>转换为AnyToken
    init?<T>(_ token: Token<T>) {
        if let appToken = token as? ApplicationToken {
            self = .application(appToken)
        } else if let categoryToken = token as? ActivityCategoryToken {
            self = .category(categoryToken)
        } else if let webToken = token as? WebDomainToken {
            self = .webDomain(webToken)
        } else {
            return nil
        }
    }
    
    // 获取原始Token<T>(可选)
    func token<T>() -> Token<T>? {
        switch self {
        case .application(let token): return token as? Token<T>
        case .category(let token): return token as? Token<T>
        case .webDomain(let token): return token as? Token<T>
        }
    }
}

扩展ActivitySelection获取统一的Token集合:

extension ActivitySelection {
    var allTokens: Set<AnyToken> {
        let appTokens = applicationTokens.compactMap(AnyToken.init)
        let categoryTokens = categoryTokens.compactMap(AnyToken.init)
        let webTokens = webDomainTokens.compactMap(AnyToken.init)
        return Set(appTokens + categoryTokens + webTokens)
    }
}

使用示例:

let selection = ActivitySelection(...)
let allTokens = selection.allTokens

// 遍历处理所有Token
for token in allTokens {
    switch token {
    case .application(let appToken):
        // 处理ApplicationToken逻辑
        break
    case .category(let categoryToken):
        // 处理ActivityCategoryToken逻辑
        break
    case .webDomain(let webToken):
        // 处理WebDomainToken逻辑
        break
    }
}

方法3:类型擦除实现通用Token集合

通过协议实现类型擦除,用统一的协议类型存储不同的Token<T>:

protocol AnyTokenProtocol: Equatable, Hashable {
    var base: Any { get }
}

extension Token: AnyTokenProtocol {
    var base: Any { self }
}

// 实现协议的Equatable和Hashable默认逻辑
func == (lhs: AnyTokenProtocol, rhs: AnyTokenProtocol) -> Bool {
    guard let lhsHashable = lhs as? any Hashable, let rhsHashable = rhs as? any Hashable else {
        return false
    }
    return lhsHashable == rhsHashable
}

extension AnyTokenProtocol {
    func hash(into hasher: inout Hasher) {
        (self as? any Hashable)?.hash(into: &hasher)
    }
}

扩展ActivitySelection获取通用Token集合:

extension ActivitySelection {
    var allTokens: Set<any AnyTokenProtocol> {
        Set(applicationTokens.map { $0 as any AnyTokenProtocol } +
            categoryTokens.map { $0 as any AnyTokenProtocol } +
            webDomainTokens.map { $0 as any AnyTokenProtocol })
    }
}

内容的提问来源于stack exchange,提问作者Jay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 04:02:01