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

Swift Combine, how to combine publishers and sink when only one publisher's value changes?

我找到了使用 MergeManyCombineLatest 合并发布者的方法,但我似乎没有找到针对我的特定情况的解决方案。

示例:

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

    @Published var allValid: Bool = false
}

我希望 allValid 在任何以前的发布者设置为 false 时变为 false,如果所有发布者都设置为 true,则 true . 我也不想对我正在观察的发布者列表进行硬编码,因为我想要一个灵活的解决方案,所以我希望能够将 Bool 发布者数组传递给我用来执行此操作的任何代码。

我试过了

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

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

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

使其正常工作

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

从技术上讲,这不是您最后一个问题的答案,但是 属性 allValid 作为计算 属性 不是更有意义吗?

var allValid: Bool {
   firstNameValid && lastNameValid && emailValid
}

这将确保 allValid 始终表示其他三个属性的逻辑与。我希望我已经理解了你问题的核心,这对你有所帮助。

CombineLatest 确实正确。您有三个发布者,因此您将使用 CombineLatest3。在这个例子中,我使用 CurrentValueSubject publishers 而不是 @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 { [[=10=].0, [=10=].1, [=10=].2] }
            .map { [=10=].allSatisfy {[=10=]}}
            .sink { print([=10=])}
            .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
                        }
                    }
                }
            }
        }
    }
}

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

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

    pub
        .map { [=11=].allSatisfy {[=11=]}}
        .sink { print([=11=])}
        .store(in: &self.storage)