Swift 5.7 any existential搭配泛型视图报协议不遵循问题咨询
Swift 中any Protocol形式的存在类型(existential)本身不会自动遵循对应的协议。即使Swift 5.7支持隐式打开存在类型特性,泛型类型的形参要求传入的是「明确遵循该协议的具体类型」,而any Provider<String>是可以承载任意遵循Provider<String>类型实例的容器类型,它自身不满足Provider的协议遵循要求,因此直接传入会触发Type 'any Provider<String>' cannot conform to 'Provider'编译错误。
另外需要注意:隐式打开存在类型仅在函数调用传参的特定场景生效,SwiftUI的View构造过程中,存储属性的类型是编译时静态确定的,编译器无法拿到s.stringProvider底层的具体Provider实现类型,因此不会自动触发隐式解包逻辑。
以下两种方案均不需要回退老式类型擦除实现,且完全保留S.stringProvider可动态赋值不同Provider实例的特性:
方案1:扩展存在类型遵循协议(推荐)
直接为any Provider<String>扩展实现Provider协议即可,由于该存在类型的关联类型Value固定为String,完全满足协议的关联类型要求:
extension any Provider<String>: Provider { func get() -> String { self.get() } }
添加该扩展后,原有报错的ProviderView(provider: s.stringProvider)代码可直接编译通过,无需修改其他业务逻辑,适配Swift 5.7+(Xcode 14及以上)环境。
方案2:为视图新增存在类型专属构造器
如果不希望全局扩展存在类型的协议遵循,可以在ProviderView内部新增专门接收存在类型的构造器,用私有轻量包装承载逻辑,不对外暴露类型擦除细节:
struct ProviderView<P: Provider>: View { let provider: P var body: some View { Text(String(describing: type(of: provider.get()))) } init(provider: any Provider<String>) where P == AnyStringProvider { self.provider = AnyStringProvider(wrapped: provider) } } // 仅内部使用的包装类型,无需对外暴露 private struct AnyStringProvider: Provider { typealias Value = String let wrapped: any Provider<String> func get() -> String { wrapped.get() } }
该方案将存在类型的适配逻辑收敛在视图内部,不会影响全局的协议实现。
原有代码的设计思路没有错误,核心是没有补齐存在类型到协议的遵循映射,不需要回退到手动编写全量类型擦除类型的旧实现。两种方案都支持在运行时给stringProvider赋值任意遵循Provider<String>的不同实现,不会丢失动态特性。
内容的提问来源于stack exchange,提问作者user16889355

