Swift Combine,当只有一个publisher的值发生变化时,如何合并publisher和sink?

Jor*_*ans 4 swift combine

我找到了使用MergeManyor组合发布商的方法CombineLatest,但在我的特定情况下似乎没有找到解决方案。

例子:

class Test {
    @Published var firstNameValid: Bool = false
    @Published var lastNameValid: Bool = false
    @Published var emailValid: Bool = false

    @Published var allValid: Bool = false
}
Run Code Online (Sandbox Code Playgroud)

我想allValid成为false当任何以前的出版商被设置为false,并且true如果他们全部都是true。我也不想对我正在观察的发布商列表进行硬编码,因为我想要一个灵活的解决方案,所以我希望能够将Bool发布商数组传递给我用来执行此操作的任何代码。

我试过这个

        let fieldPublishers = [$firstNameValid, $lastNameValid, $emailValid]
        Publishers
            .MergeMany(fieldPublishers)
            .sink { [weak self] values in
                self?.allValid = values.allSatisfy { $0 }
            }
            .store(in: &subscribers)
Run Code Online (Sandbox Code Playgroud)

但这当然行不通,因为我得到的是发布者数组,而不是值数组。sink我尝试了一些其他方法(忘记了哪些方法),但它们似乎只有在我在执行期间为所有 3 个发布者分配了一个值时才会调用。

在只使用 2 个发布者的情况下,我设法使用CombineLatest.

所以问题是:sink当数组中只有一个发布者在实例化后更改值时Test,我是否可以触发,然后迭代我正在观察的所有发布者的值?

mat*_*att 9

结合最新确实是正确的。您有三个发布商,因此您将使用CombineLatest3。在这个例子中,我使用 CurrentValueSubjectpublishers 而不是@Published,但原理是一样的:

import UIKit
import Combine

func delay(_ delay:Double, closure:@escaping ()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
class ViewController: UIViewController {
    let b1 = CurrentValueSubject<Bool,Never>(false)
    let b2 = CurrentValueSubject<Bool,Never>(false)
    let b3 = CurrentValueSubject<Bool,Never>(false)
    var storage = Set<AnyCancellable>()
    override func viewDidLoad() {
        super.viewDidLoad()

        Publishers.CombineLatest3(b1,b2,b3)
            .map { [$0.0, $0.1, $0.2] }
            .map { $0.allSatisfy {$0}}
            .sink { print($0)}
            .store(in: &self.storage)
        
        // false
        delay(1) {
            self.b1.send(true) // false
            delay(1) {
                self.b2.send(true) // false
                delay(1) {
                    self.b3.send(true) // true
                    delay(1) {
                        self.b1.send(false) // false
                        delay(1) {
                            self.b1.send(true) // true
                        }
                    }
                }
            }
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

好吧,现在你可能会抱怨,这对于硬编码的三个发布商来说没问题,但我想要任意数量的发布商。美好的!从一组发布者开始,使用 JointLatest 一次累积一个发布者,以形成一个生成 Bool 数组的发布者:

    let list = [b1, b2, b3] // any number of them can go here!
    let pub = list.dropFirst().reduce(into: AnyPublisher(list[0].map{[$0]})) {
        res, b in
        res = res.combineLatest(b) {
            i1, i2 -> [Bool] in
            return i1 + [i2]
        }.eraseToAnyPublisher()
    }

    pub
        .map { $0.allSatisfy {$0}}
        .sink { print($0)}
        .store(in: &self.storage)
Run Code Online (Sandbox Code Playgroud)