Swift5.7下ForEach遍历[any Tabbable]报Identifiable适配错误求解
问题根因
报错来自Swift 5.7存在类型的特性限制:带关联类型的协议对应的any存在类型,本身不会遵循它所包装的协议。
[any Tabbable]的元素是存在类型容器,可以装入任意符合Tabbable的具体类型实例,但容器类型any Tabbable自身没有实现Identifiable的协议要求。- 数组的
forEach方法仅要求可遍历取出元素,不校验元素是否遵循Identifiable,因此可以正常执行。 - SwiftUI
ForEach的无id初始化器要求集合元素必须静态遵循Identifiable,因此会抛出Type 'any Tabbable' cannot conform to 'Identifiable'编译错误。
可行解决方案
按改造成本从低到高排列:
显式传入ForEach的id路径,零修改现有逻辑
直接调用ForEach带id参数的初始化器,绕开元素必须遵循Identifiable的约束。由于Identifiable协议本身要求id字段必须为Hashable,直接传入id的key path即可正常编译运行:ForEach(viewModel.models, id: \.id) { _ in Text("Test") }这是Swift 5.7环境下成本最低的方案,不需要调整现有协议定义、ViewModel属性结构。
实现AnyTabbable类型擦除包装
如果需要频繁遍历该数组、不想每次调用ForEach都重复传入id参数,可以实现类型擦除的具体类型,将异构的any Tabbable实例统一包装为明确遵循Tabbable的具体类型:public struct AnyTabbable: Tabbable { public let id: AnyHashable private let base: any Tabbable public init(_ base: any Tabbable) { self.base = base self.id = AnyHashable(base.id) } // 在此处转发Tabbable协议定义的其余属性、方法,对应调用base的实现即可 }使用时将ViewModel中的数组类型修改为
[AnyTabbable],赋值时给每个元素包裹一层AnyTabbable()初始化,后续调用ForEach时就可以直接传数组、不需要额外传id参数。版本升级补充:Swift 5.9及以上版本新增了部分存在类型自动遵循协议的能力,部分场景下不需要上述处理即可直接编译,但Swift 5.7不支持该特性。另外不要尝试用
[some Tabbable]修复问题,该语法要求数组内所有元素为同一个具体的Tabbable类型,无法存放多个不同实现的异构实例,不符合这类场景的使用需求。
内容的提问来源于stack exchange,提问作者BrendanS
相关产品推荐
相关产品推荐

