如何使用 rxSwift 压缩、合并或连接而不覆盖以前的数据?

How do I zip, merge or concat using rxSwift without overriding previous data?

我有三个 Observable<[Recipe]> 流,我想将它们合并为一个流 Observable<[Recipe]>

let breakfast = Database.database().reference().rx.fetchFireBaseData(recipeType: .breakfast)
    .map { [=10=].filter { UserDefaults.standard.favoriteRecipes.contains([=10=].fbKey) }}
        
let dessert = Database.database().reference().rx.fetchFireBaseData(recipeType: .dessert)
    .map { [=10=].filter { UserDefaults.standard.favoriteRecipes.contains([=10=].fbKey) }}

let cookies = Database.database().reference().rx.fetchFireBaseData(recipeType: .cookies)
    .map { [=10=].filter { UserDefaults.standard.favoriteRecipes.contains([=10=].fbKey) }}

如果我使用Observable.zip(breakfast, dessert, cookies).do(onNext: { [weak self] value in... 我得到 ([Recipe], [Recipe], [Recipe])

如果我使用Observable.merge(breakfast, dessert, cookies).do(onNext: { [weak self] value in... 我依次获取流,这意味着最后一个完成的会覆盖前两个。

如果我使用Observable.concat(breakfast, dessert, cookies).do(onNext: { [weak self] value in... 我得到了与 merge.

相同的行为

那么如何将三个流合并为一个流而不让它们相互覆盖?

为了更好地了解我要做什么:

return Observable.merge(breakfast, cookies, dessert).do(onNext: { [weak self] value in
    self?.content.accept(value.compactMap { FavoritesCollectionViewCellViewModel(favorite: [=11=])})
})

我想 accept 所有组合流到 behaviorRelay,映射到 cell 数据。然后 content 中继 boundcollectionView。观看 collectionView 时,使用 concat 我可以短暂地看到第一和第二个流,然后它们被最后一个流替换。

好的,不知道这是否是解决它的最漂亮的方法,但我使用了 zip 然后只是在 compactMap

中将数组彼此相加
return Observable.zip(breakfast, dessert, cookies)
    .compactMap { [=10=].0 + [=10=].1 + [=10=].2 }
    .do(onNext: { [weak self] value in
        self?.content.accept(value.compactMap { FavoritesCollectionViewCellViewModel(favorite: [=10=])})
})

您可能不知道,combineLatest(和 zip)有一个用于处理输入的最终参数。所以你可以这样做:

let allRecipes = Observable.combineLatest(breakfast, dessert, cookies) { [=10=] +  +  }

相当于:

Observable.combineLatest(breakfast, dessert, cookies)
        .map { [=11=].0 + [=11=].1 + [=11=].2 }

这与你最终得到的基本相同,除了你使用 zip 而不是 combineLatest

使用zip 假设所有三个 Observable 将发出完全相同数量的元素。通过使用 combineLatest,您假设每个 Observable 至少发射一个元素,但它们不必都发射相同的数字。

如果您想避免这种假设,请为每个来源添加一个 .startWith

Observable.combineLatest(
    breakfast.startWith([]),
    dessert.startWith([]),
    cookies.startWith([])
) { [=12=] +  +  }

有了上面的内容,只要任何一个源 observable 发出,您就会在输出中得到一些东西,并且每次其他发出时都会得到另一个更大的数组。

请注意,在以上所有内容(包括您的回答)中,任何一个源可观察对象中的错误都会导致整个事情出错。如果你想避免这种情况,你将不得不捕获每个源 Observable 的错误。该代码的确切外观取决于您要对错误执行的操作。