触发 CombineLatest 在 Combine 中传播初始值

Posted

技术标签:

【中文标题】触发 CombineLatest 在 Combine 中传播初始值【英文标题】:Triggering CombineLatest to propagate initial value in Combine 【发布时间】:2019-06-30 08:57:20 【问题描述】:

我有两个字符串发布者和一个返回 AnyPublisher 的计算属性。逻辑很简单,但我想知道是否有任何方法可以传播初始值。我认为这应该是可能的,因为发布者有初始值。

在 VC 中,我从 ViewModel(来自 textField)为发布者分配新值。

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 !($0.isEmpty || $1.isEmpty)
        .eraseToAnyPublisher()

validatedText 仅在我开始在两个文本字段中输入时才开始发布新值。例如,我尝试在 VM 的 init 中分配一些新值(给第一个和第二个 Publisher),但它也不起作用。有没有办法做到这一点,否则我将不得不在不使用组合的情况下设置按钮的初始状态(禁用它)?

【问题讨论】:

【参考方案1】:

不幸的是,这似乎只是 @Published 的行为,但您可以在生成的 Publisher 中通过添加初始值来解决此问题:

var validatedText: AnyPublisher<Bool, Never> 
    let validate: (String, String) -> Bool = 
        !($0.isEmpty || $1.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)
    

【讨论】:

以上是关于触发 CombineLatest 在 Combine 中传播初始值的主要内容,如果未能解决你的问题,请参考以下文章

在啥情况下我可以使用 zip 和 combineLatest? RxSwift

关于 RxSwift/RxCocoa 与 combineLatest 绑定的问题

Rx.combineLatest2 未按预期工作

combineLatest不会发出最新值

使用 combineLatest 和 Rxjs 返回 observable 的结果

订阅 combineLatest 中的所有 observables