The Reactive programming is getting popular as it is a completely different paradigm. Programmers use imperative programming quite a long time and it is prevailing nowadays.
We are observing the RxSwift version 4.0 along with RxCocoa 4.0. The latter is the set of extensions for Cocoa elements such as UITextField, tap events etc.
The key concept in reactive programming is data streams, and propagation via observable sequences. (http://reactivex.io/)
Observable – a structure which emits a certain value over time. A subscriber subscribes to the observable. We can observe an array, string, HTTP request, text change in UITextField.
The basic example of Observable which emits a string.
let observable = Observable<String>.just("Observe me") _ = observable.subscribe(onNext: { value in print("onNext: \(value)") }, onError: { error in print(error) }, onCompleted: { print("Completed") }) { print("Disposed") }
The observable has 3 types of events:
- onNext – emits N number of times an event
- onError – the observable emits an error and terminates
- onCompleted – the observable completes its work.
From the output above we see next:
onNext: Observe me Completed Disposed
The observable emitted a string value, completed itself and was disposed. The disposing is a memory management mechanism used in RxSwift.
We can create a DisposeBag which is retained by some parent object, it can be UIViewController in many cases. And when the object is deallocated,deinit
is called then disposebag
disposes subscriptions.
And this will look like:
let disposeBag = DisposeBag() let observable = Observable<String>.just("Observe me") observable.subscribe(onNext: { value in print("onNext: \(value)") }) { print("Disposed") } .disposed(by: disposeBag)
You can see that we can omit the error and completion handling.
What about threading on RxSwift? It uses Schedulers. An elegant way to subscribe and observe on different threads.
subscribeOn
is used to specify Scheduler of the work before the subscription happens.
observeOn
is processing subscription itself.
There are a bunch of schedulers ready to use MainScheduler, CurrentThreadScheduler, ConcurrentMainScheduler. They are similar to the Grand Central Dispatch.
Let’s create an observable for text input into the UITextField. After a user inputs some text, then a search can generate an HTTP call. Many requests can cause network contamination issues. So we will find a plethora of operators (http://reactivex.io/documentation/operators.html#tree). For instance, throttle
would help us filter out many operations.
Adding a simple UITextField to the view controller and with RxCocoa we can access text changes. Such as ‘textField.rx.text’ and it is a ControlProperty:
Sequence of values only represents initial control value and user initiated value changes. Programmatic value changes won’t be reported. It’s properties are: – it never fails –
shareReplay(1)
behavior
- it’s stateful, upon subscription (calling subscribe) last element is immediately replayed if it was produced
- it will
Complete
sequence on control being deallocated- it never errors out
- it delivers events on
MainScheduler.instance
ControlProperty
is an example of Hot observable
. It would produce sequence of elements no matter if it has a subscriber. And the result is shared between subscribers via the operator shareReplay(1)
. It means whenever a new observer is subscribed a last value in a sequence would be replayed and thus shared. And it relates to producing heat.
The Cold observable
doesn’t produce work or use resources until there is a subscriber. It can be async operations as HTTP requests. And it is not shared by default.
The code for transforming a string sequence from UITextField.
textField.rx.text.orEmpty .subscribe(onNext: { text in print("text: \(text)") }) .disposed(by: disposeBag)
rx.text
– a ControlProperty to observe text changes
orEmpty
– transforms control property to from String? → String
Make sure that disposeBag
is declared on a viewcontroller level, not a local variable. So the subscription would have the same lifecycle with UIViewController.
textField.rx.text.orEmpty .throttle(1, scheduler: MainScheduler.instance) .map({ text in return text.count }) .subscribe(onNext: { text in print("text length: \(text)") }) .disposed(by: disposeBag)
The throttling of input can be achieved with the operator whichthrottle(1, scheduler: MainScheduler.instance)
simply filters out the emitted events in 1-second duration between emitting many values.
The map
operator transforms values send it further to the subscriber.
.map({ text in return text.count })
can be written like .map { $0.count }
So this simple example gave an overview of the technology. The one side of using Reactive patterns is data binding. In the MVVM architecture, the RxSwift can process receiving events from the user interface and bind
values.
textField.rx.text.orEmpty .map { "character count: \($0.count)" } .bind(to: textLabel.rx.text) .disposed(by: disposeBag)
This example shows a binding between the text emitted from the text field and a text variable on a label. In such a way, we have easy access to event streams and direction of using it via binders
.
The RxSwift is a powerful library with a unique paradigm. We can write a succinct and easy to read code. But the developer still needs to keep in mind retain cycles, error handling and nature of observables aka hot and cold.
Pingback: Magic of RxSwift. Rx Operators | agostini.tech
You make RxSwift bearable to beginners! THANK YOU! Keep it up!