[英]Swift Combine, how to combine publishers and sink when only one publisher's value changes?
I have found ways of combining publishers using MergeMany
or CombineLatest
, but I don't seem to find a solution in my particular case.我已经找到了使用
MergeMany
或CombineLatest
组合发布者的方法,但在我的特定情况下我似乎没有找到解决方案。
Example:例子:
class Test {
@Published var firstNameValid: Bool = false
@Published var lastNameValid: Bool = false
@Published var emailValid: Bool = false
@Published var allValid: Bool = false
}
I want allValid
to become false
when any of the previous publishers are set to false
, and true
if all of them are true
.当任何以前的发布者设置为
false
时,我希望allValid
变为false
,如果它们都设置为true
,则为true
。 I also don't want to hardcode the list of publishers I am observing since I want a flexible solution, so I want to be able to pass an array of Bool
publishers to whatever code I use to do this.我也不想硬编码我正在观察的发布者列表,因为我想要一个灵活的解决方案,所以我希望能够将一组
Bool
发布者传递给我用来执行此操作的任何代码。
I tried this我试过这个
let fieldPublishers = [$firstNameValid, $lastNameValid, $emailValid]
Publishers
.MergeMany(fieldPublishers)
.sink { [weak self] values in
self?.allValid = values.allSatisfy { $0 }
}
.store(in: &subscribers)
But this of course doesn't work because I get an array of publishers and not an array of values.但这当然不起作用,因为我得到了一组发布者而不是一组值。 I tried some other ways (forgot which ones) but they only seemed to call
sink
if I assigned a value during execution to all 3 publishers.我尝试了其他一些方法(忘记了哪些方法),但如果我在执行期间为所有 3 个发布者分配了一个值,它们似乎只会调用
sink
。
In the case of only using 2 publishers I managed to get it working using CombineLatest
.在仅使用 2 个发布者的情况下,我设法使用
CombineLatest
使其工作。
So the question is: Can I have sink
triggered when only one of the publishers in an array changes value after instantiation of Test
, and then iterate over the values of all the publishers I am observing?所以问题是:当数组中只有一个发布者在
Test
实例化后更改值时,我是否可以触发接收sink
,然后遍历我正在观察的所有发布者的值?
CombineLatest is indeed correct. CombineLatest 确实是正确的。 You have three publishers so you would use CombineLatest3.
您有三个发布者,因此您将使用 CombineLatest3。 In this example, I use CurrentValueSubject publishers instead of
@Published
, but it's the same principle:在此示例中,我使用 CurrentValueSubject 发布者而不是
@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
}
}
}
}
}
}
}
Okay, now you may complain, that's okay for a hard-coded three publishers, but I want any number of publishers.好的,现在您可能会抱怨,对于硬编码的三个发布者来说没关系,但我想要任意数量的发布者。 Fine, Start with an array of your publishers: accumulate them one at a time with CombineLatest to form a publisher that produces an array of Bool:
好的,从您的发布者数组开始:使用 CombineLatest 一次累积一个以形成一个发布者,该发布者生成一个 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)
This is technically not an answer to your last question but wouldn't the property allValid make more sense as a computed property?从技术上讲,这不是您最后一个问题的答案,但属性 allValid 作为计算属性是否更有意义?
var allValid: Bool {
firstNameValid && lastNameValid && emailValid
}
This would make sure, that allValid at all times represents the logical AND for the other three properties.这将确保 allValid 始终代表其他三个属性的逻辑与。 I hope that I have understood the core of your question and this helped.
我希望我已经理解了您问题的核心,这对您有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.