如何对两个合并的Publisher应用compactMap操作符?
Combine:仅当两个Publisher均输出非nil值时触发Sink
我们有以下两个发布者:
var firstFeed = PassthroughSubject<Int?, Never>() var secondFeed = PassthroughSubject<Int?, Never>()
需求是创建一个订阅,只有当两个发布者都输出非nil值时,才调用sink处理数据。
当前实现
目前的实现方式是先分别对两个发布者做compactMap过滤nil,再合并:
let sub = firstFeed .compactMap{$0} .combineLatest(secondFeed.compactMap({$0})) .sink { firstValue, secondValue in print("we sunk with \(firstValue), \(secondValue)") }
测试数据流
我们输入以下测试数据:
firstFeed.send(1) secondFeed.send(nil) secondFeed.send(2) firstFeed.send(nil) firstFeed.send(3) firstFeed.send(nil) secondFeed.send(nil) firstFeed.send(nil) secondFeed.send(nil) secondFeed.send(4) firstFeed.send(nil) secondFeed.send(nil) firstFeed.send(5) secondFeed.send(6) secondFeed.send(nil) firstFeed.send(nil) firstFeed.send(7) firstFeed.send(nil) secondFeed.send(8)
当前输出
执行后得到符合预期的输出:
we sunk with 1, 2 we sunk with 3, 2 we sunk with 3, 4 we sunk with 5, 4 we sunk with 5, 6 we sunk with 7, 6 we sunk with 7, 8
期望写法
希望能先合并两个发布者,再通过一次compactMap过滤掉包含nil的组合,但不知道compactMap的闭包该如何编写:
let sub = firstFeed .combineLatest(secondFeed) .compactMap{?} .sink { firstValue, secondValue in print("we sunk with \(firstValue), \(secondValue)") }
解决方案
可以在compactMap中对两个值同时进行非nil判断,只有当两者都有效时返回元组,否则返回nil,以此过滤掉无效组合。
标准写法
let sub = firstFeed .combineLatest(secondFeed) .compactMap { first, second in guard let first = first, let second = second else { return nil } return (first, second) } .sink { firstValue, secondValue in print("we sunk with \(firstValue), \(secondValue)") }
简洁写法
利用Swift的类型转换特性,将(Int?, Int?)类型的元组尝试转型为(Int, Int),转型失败时返回nil,从而被compactMap过滤:
let sub = firstFeed .combineLatest(secondFeed) .compactMap { $0 as? (Int, Int) } .sink { firstValue, secondValue in print("we sunk with \(firstValue), \(secondValue)") }
两种写法的执行效果和原实现完全一致,都能得到预期的输出。
内容的提问来源于stack exchange,提问作者Reza Shirazian
相关产品推荐
相关产品推荐

