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 Observable
s, but not really. There are some upgraded Observable
s called ControlEvent
s (which represent events) and ControlProperties
(which represent properties, surprise!). These things holds Observable
streams under the hood, but also have some nuances:
Complete
sequence on control being deallocated.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.