【问题标题】:How to make a Publisher from array of Publishers?如何从发布者数组中创建发布者?
【发布时间】:2025-12-07 07:50:02
【问题描述】:

我正在使用 Swift Combine 并希望创建一个从 Publisher 数组创建的 Publisher。每当数组中的一个发布者发出一个新元素时,它应该发出一个元素数组。

RxSwift 中的内容如下:

 let obs1 = PublishSubject<Int>()
 let obs2 = PublishSubject<Int>()
 let arrayObs = [obs1, obs2)]

 Observable.combineLatest(arrayObs)
    .subscribe(onNext: { arrayOfLatest in
        print(arrayOfLatest) //prints an array of integers
    }).disposed(by: disposeBag)

obs1.onNext(5) 
obs2.onNext(10) // prints [5,10]
obs1.onNext(12) // prints [12,10]

【问题讨论】:

    标签: ios swift reactive-programming combine


    【解决方案1】:

    只要您对“数组”部分的要求不高,combineLatestzip 也适用于 Combine 框架。它们之间的区别在于您是否希望每次发出的总价值包含来自每个发布者的最旧 (zip) 或最新 (combineLatest) 贡献。

    您的示例实际上并没有提供足够的信息来说明您想要哪些。要知道你想要哪个,你需要说出当你说时会发生什么,例如:

    obs1.onNext(5) 
    obs1.onNext(6) 
    obs2.onNext(10)
    

    尽管如此,Combine 是 Swift,所以它使用 元组, 而不是数组来表示值的整体。但是,如果像您的示例一样,所有发布者都具有相同的输出和失败类型,则可以通过映射轻松替换数组。有关将 zip 转换为数组处理器的示例,请参见 How to zip more than 4 publishers。完全相同的技术适用于combineLatest

    所以,这是一个实际的工作示例;为了使示例比您的更通用,我使用了三个发布者而不是两个:

    class ViewController: UIViewController {
        let obs1 = PassthroughSubject<Int,Never>()
        let obs2 = PassthroughSubject<Int,Never>()
        let obs3 = PassthroughSubject<Int,Never>()
        var storage = Set<AnyCancellable>()
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let arr = [obs1, obs2, obs3]
            let pub = arr.dropFirst().reduce(into: AnyPublisher(arr[0].map{[$0]})) {
                res, ob in
                res = res.combineLatest(ob) {
                    i1, i2 -> [Int] in
                    return i1 + [i2]
                }.eraseToAnyPublisher()
            }
            pub.sink { print($0) }.store(in: &storage)
            obs1.send(5)
            obs2.send(10)
            obs3.send(1) // [5, 10, 1]
            obs1.send(12) // [12, 10, 1]
            obs3.send(20) // [12, 10, 20]
        }
    }
    

    【讨论】:

      【解决方案2】:

      使用 combineLatest 非常简单。传递的值是一个元组,而不是一个数组。

      import Foundation
      import Combine
      
      let obs1 = PassthroughSubject<Int, Never>()
      let obs2 = PassthroughSubject<Int, Never>()
      
      let sub = obs1.combineLatest(obs2).sink { (first, second) in
          print("\(first) \(second)")
      }
      
      obs1.send(5)
      obs2.send(10) // prints 5 10
      obs1.send(12) // prints 12 10
      

      由于您来自 RxSwift,因此此备忘单可能会对 https://github.com/CombineCommunity/rxswift-to-combine-cheatsheet 有所帮助

      【讨论】:

      • 不幸的是,没有一个 combineLatest() 可以根据需要接受一组发布者。它们仅限于组合 2-4 个发布者并且来自必须在代码中指定的参数列表。