Swift Combine,当只有一个publisher的值发生变化时,如何合并publishers和sink?
Swift Combine, how to combine publishers and sink when only one publisher's value changes?
我找到了使用 MergeMany
或 CombineLatest
合并发布者的方法,但我似乎没有找到针对我的特定情况的解决方案。
示例:
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)
我找到了使用 MergeMany
或 CombineLatest
合并发布者的方法,但我似乎没有找到针对我的特定情况的解决方案。
示例:
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)