Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Combine - How to get a Publisher that delivers events for every character change of UITextField's text property

I noticed that

textField.publisher(for: \.text)

delivers events when editing finishes, but not for every character/editing change. How do I get a Publisher, that sends evens for every change? In ReactiveSwift it would be

textField.reactive.continousTextValues()

And in RxSwift it would be just (How do you get a signal every time a UITextField text property changes in RxSwift)

textField.rx.text

Approaches I took:

  • checking the publisher(for:options:) method, but there are no appropriate options for the desired outcome.
  • adding a target/action textField.addTarget(self, action: #selector(theTextFieldDidChange), for: .editingChanged) (UITextField text change event)
  • doing essentially the same as the previous step by connecting the action via interface builder, both leading to extra work and cluttered code.
  • Watching the 2019 WWDC videos about Combine. They were not dealing with textfields, but using @Published variables instead, hiding were the values actually came from - (or did I miss something?).

I have no clue at the moment, how to do this and I feel the tendency to go back to ReactiveSwift, but I wanted to ask you, before taking this step backwards.

like image 832
thetrutz Avatar asked Mar 11 '20 16:03

thetrutz


3 Answers

In case of a UITextField you can use this extension:

  extension UITextField {
    func textPublisher() -> AnyPublisher<String, Never> {
        NotificationCenter.default
            .publisher(for: UITextField.textDidChangeNotification, object: self)
            .map { ($0.object as? UITextField)?.text  ?? "" }
            .eraseToAnyPublisher()
    }
  }

use as: textField.textPublisher()

like image 114
kprater Avatar answered Sep 23 '22 07:09

kprater


Unfortunately, Apple didn't add publishers for this to UIKit and because UIControl doesn't implement KVO the publisher(for:) publisher doesn't work quite as expected. If you're okay with adding a dependency to your project there are a couple of good UIControl publishers here.

Alternatively, you could implement your own publisher that does this, or subscribe to textfield changes the old school way.

like image 30
donnywals Avatar answered Sep 23 '22 07:09

donnywals


You can always create a custom Publisher for your needs. For example, here I've created TextField publisher, that wraps textFieldDidChange action for textField and sends String after each character entered/deleted! Please, copy the link, SO doesn't parse it:

https://github.com/DmitryLupich/Combine-UIKit/blob/master/CombineCustomPublishers/🚜%20Publishers/TextFieldPubisher.swift

like image 27
Dmitriy Lupych Avatar answered Sep 25 '22 07:09

Dmitriy Lupych