触发 CombineLatest 在 Combine 中传播初始值
Triggering CombineLatest to propagate initial value in Combine
我有两个字符串发布者和一个计算 属性 其中 returns AnyPublisher。逻辑很简单,但我想知道是否有任何方法可以传播初始值。我认为这应该是有可能的,因为发布者有初始值。
在 VC 中,我从 ViewModel(从 textField)为 Publishers 分配新值。
firstTextField.addTarget(self, action: #selector(firstTextFieldDidChange(_:)), for: .editingChanged)
secondTextField.addTarget(self, action: #selector(secondTextFieldDidChange(_:)), for: .editingChanged)
@objc private func firstTextFieldDidChange(_ textField: UITextField) {
viewModel.firstPublisher = textField.text ?? ""
}
@objc private func secondTextFieldDidChange(_ textField: UITextField) {
viewModel.secondPublisher = textField.text ?? ""
}
然后我将 Publisher (combineLatest) 分配给我的按钮:
_ = viewModel.validatedText
.receive(on: RunLoop.main)
.assign(to: \.isEnabled, on: button)
在 VM 中我有两个发布者:
@Published var firstPublisher: String = ""
@Published var secondPublisher: String = ""
和 CombineLatest:
var validatedText: AnyPublisher<Bool, Never> {
return Publishers.CombineLatest($firstPublisher, $secondPublisher) {
return !([=13=].isEmpty || .isEmpty)
}.eraseToAnyPublisher()
}
validatedText 仅在我开始在两个文本字段中输入时才开始发布新值。例如,我尝试在 VM 的 init 中分配一些新值(给第一和第二发布者),但它也没有用。有什么办法可以做到这一点,或者我必须在不使用组合的情况下设置按钮的初始状态(禁用它)?
不幸的是,这似乎只是 @Published
的行为,但您可以通过在生成的 Publisher 中添加一个初始值来解决此问题:
var validatedText: AnyPublisher<Bool, Never> {
let validate: (String, String) -> Bool = {
!([=10=].isEmpty || .isEmpty)
}
return Publishers.CombineLatest($firstPublisher, $secondPublisher, transform: validate)
.prepend(validate(firstPublisher, secondPublisher))
.eraseToAnyPublisher()
}
相反,如果您更愿意采用这种方法,那么编写自己的 属性 委托来获得您想要的行为是相当简单的:
import Combine
@propertyDelegate
struct InitialPublished<Value> : Publisher {
typealias Output = Value
typealias Failure = Never
private let subject: CurrentValueSubject<Output, Failure>
var value: Value {
set { subject.value = newValue }
get { subject.value }
}
init(initialValue: Value) {
subject = CurrentValueSubject(initialValue)
}
func receive<S>(subscriber: S) where S: Subscriber, Value == S.Input, Failure == S.Failure {
subject.receive(subscriber: subscriber)
}
}
我有两个字符串发布者和一个计算 属性 其中 returns AnyPublisher。逻辑很简单,但我想知道是否有任何方法可以传播初始值。我认为这应该是有可能的,因为发布者有初始值。
在 VC 中,我从 ViewModel(从 textField)为 Publishers 分配新值。
firstTextField.addTarget(self, action: #selector(firstTextFieldDidChange(_:)), for: .editingChanged)
secondTextField.addTarget(self, action: #selector(secondTextFieldDidChange(_:)), for: .editingChanged)
@objc private func firstTextFieldDidChange(_ textField: UITextField) {
viewModel.firstPublisher = textField.text ?? ""
}
@objc private func secondTextFieldDidChange(_ textField: UITextField) {
viewModel.secondPublisher = textField.text ?? ""
}
然后我将 Publisher (combineLatest) 分配给我的按钮:
_ = viewModel.validatedText
.receive(on: RunLoop.main)
.assign(to: \.isEnabled, on: button)
在 VM 中我有两个发布者:
@Published var firstPublisher: String = ""
@Published var secondPublisher: String = ""
和 CombineLatest:
var validatedText: AnyPublisher<Bool, Never> {
return Publishers.CombineLatest($firstPublisher, $secondPublisher) {
return !([=13=].isEmpty || .isEmpty)
}.eraseToAnyPublisher()
}
validatedText 仅在我开始在两个文本字段中输入时才开始发布新值。例如,我尝试在 VM 的 init 中分配一些新值(给第一和第二发布者),但它也没有用。有什么办法可以做到这一点,或者我必须在不使用组合的情况下设置按钮的初始状态(禁用它)?
不幸的是,这似乎只是 @Published
的行为,但您可以通过在生成的 Publisher 中添加一个初始值来解决此问题:
var validatedText: AnyPublisher<Bool, Never> {
let validate: (String, String) -> Bool = {
!([=10=].isEmpty || .isEmpty)
}
return Publishers.CombineLatest($firstPublisher, $secondPublisher, transform: validate)
.prepend(validate(firstPublisher, secondPublisher))
.eraseToAnyPublisher()
}
相反,如果您更愿意采用这种方法,那么编写自己的 属性 委托来获得您想要的行为是相当简单的:
import Combine
@propertyDelegate
struct InitialPublished<Value> : Publisher {
typealias Output = Value
typealias Failure = Never
private let subject: CurrentValueSubject<Output, Failure>
var value: Value {
set { subject.value = newValue }
get { subject.value }
}
init(initialValue: Value) {
subject = CurrentValueSubject(initialValue)
}
func receive<S>(subscriber: S) where S: Subscriber, Value == S.Input, Failure == S.Failure {
subject.receive(subscriber: subscriber)
}
}