Запуск CombineLatest для распространения начального значения в Combine - PullRequest
0 голосов
/ 30 июня 2019

У меня есть два String-издателя и одно вычисленное свойство, которое возвращает 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 (cellLatest) моей кнопке:

_ = viewModel.validatedText
   .receive(on: RunLoop.main)
   .assign(to: \.isEnabled, on: button)

В ВМ у меня есть два издателя:

@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 onlyначинает публиковать новые значения, когда я начинаю печатать в обоих текстовых полях.Я попытался присвоить некоторые новые значения в init VM, например (для первого и второго Publisher), но это также не сработало.Есть ли способ сделать это, или мне придется установить начальное состояние кнопки (отключить его) без использования комбайна?

1 Ответ

2 голосов
/ 30 июня 2019

К сожалению, похоже, что это просто поведение @Published, но вы можете обойти это в сгенерированном издателе, добавив начальное значение:

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)
    }
}
...