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
相关产品推荐
相关产品推荐

