Swift Language RxSwift RxCocoa and ControlEvents


Example

RxSwift provides not only the ways to control your data, but to represent user actions in a reactive way also.

RxCocoa contains everything you need. It wraps most of the UI components' properties into Observables, but not really. There are some upgraded Observables called ControlEvents (which represent events) and ControlProperties (which represent properties, surprise!). These things holds Observable streams under the hood, but also have some nuances:

  • It never fails, so no errors.
  • It will Complete sequence on control being deallocated.
  • It delivers events on the main thread (MainScheduler.instance).

Basically, you can work with them as usual:

button.rx_tap.subscribeNext { _ in   // control event
    print("User tapped the button!")
}.addDisposableTo(bag)

textField.rx_text.subscribeNext { text in // control property
    print("The textfield contains: \(text)")
}.addDisposableTo(bag)
// notice that ControlProperty generates .Next event on subscription
// In this case, the log will display 
// "The textfield contains: "
// at the very start of the app.

This is very important to use: as long as you use Rx, forget about the @IBAction stuff, everything you need you can bind and configure at once. For example, viewDidLoad method of your view controller is a good candidate to describe how the UI-components work.

Ok, another example: suppose we have a textfield, a button, and a label. We want to validate text in the textfield when we tap the button, and display the results in the label. Yep, seems like an another validate-email task, huh?

First of all, we grab the button.rx_tap ControlEvent:

----()-----------------------()----->

Here empty parenthesis show user taps. Next, we take what's written in the textField with withLatestFrom operator (take a look at it here, imagine that upper stream represents user taps, bottom one represents text in the text field).

button.rx_tap.withLatestFrom(textField.rx_text)

----("")--------------------("123")--->
//  ^ tap   ^ i wrote 123    ^ tap

Nice, we have a stream of strings to validate, emitted only when we need to validate.

Any Observable has such familiar operators as map or filter, we'll take map to validate the text. Create validateEmail function yourself, use any regex you want.

button.rx_tap                                // ControlEvent<Void>
        .withLatestFrom(textField.rx_text)   // Observable<String>
        .map(validateEmail)                  // Observable<Bool>
        .map { (isCorrect) in
            return isCorrect ? "Email is correct" : "Input the correct one, please"
        }                                    // Observable<String>
        .bindTo(label.rx_text)              
        .addDisposableTo(bag) 

Done! If you need more custom logic (like showing error views in case of error, making a transition to another screen on success...), just subscribe to the final Bool stream and write it there.