iOS13系统下如何为SubscribeOn扩展实现flatMap操作符
iOS13 Combine的flatMap兼容方案
现有实现评估
你当前针对Publishers.SubscribeOn的扩展实现是正确的,它手动构造了Publishers.FlatMap实例,通过SetFailureType完成了失败类型匹配,在仅针对SubscribeOn类型调用flatMap的场景下可以完全解决iOS13的编译问题,运行行为和系统iOS14+的原生flatMap一致。
但这个方案存在明显局限性:仅支持SubscribeOn类型的发布者调用,如果后续发布者链中出现Map、Filter、Decode等其他类型的发布者,依然无法调用flatMap,需要为每一个发布者类型单独写扩展,维护成本很高。
更优兼容方案
推荐直接扩展Publisher协议,实现全类型兼容的flatMap方法,一次扩展所有发布者都可使用:
extension Publisher { /// 兼容iOS13的flatMap实现,能力与系统原生API完全对齐 func compatibleFlatMap<P>( maxPublishers: Subscribers.Demand = .unlimited, _ transform: @escaping (Output) -> P ) -> Publishers.FlatMap<P, Publishers.SetFailureType<Self, P.Failure>> where P: Publisher, Failure == P.Failure { return Publishers.FlatMap( upstream: self.setFailureType(to: P.Failure.self), maxPublishers: maxPublishers, transform: transform ) } /// 重载版本,适配上游失败类型为Never的场景 func compatibleFlatMap<P>( maxPublishers: Subscribers.Demand = .unlimited, _ transform: @escaping (Output) -> P ) -> Publishers.FlatMap<P, Self> where P: Publisher, Failure == Never { return Publishers.FlatMap( upstream: self, maxPublishers: maxPublishers, transform: transform ) } }
使用方式
仅需要把原有代码中的flatMap替换为compatibleFlatMap即可直接运行:
let a1 = Just(0) .subscribe(on: DispatchQueue.global()) .compatibleFlatMap(f1) .compatibleFlatMap(f2) .receive(on: DispatchQueue.main) .sink { _ in print("\(Thread.current): completed") } receiveValue: { value in print("\(Thread.current): sink() value = \(value)") }
方案优势
- 适用所有Publisher类型,无需针对单个发布者类型做重复扩展
- 支持
maxPublishers参数,能力和系统原生API完全一致 - 覆盖了上游失败类型为Never、失败类型匹配两种常见场景
- 后续最低支持版本升级到iOS14+后,全局替换
compatibleFlatMap为flatMap即可完成迁移,成本极低
内容的提问来源于stack exchange,提问作者John Tracid
相关产品推荐
相关产品推荐

