RxSwift foundation and basic components: part 1
Reactive programming has been in the picture for more than 20 years now, but it did not really start to gain momentum until the introduction of reactive extensions in 2009. Reactive extensions or Rx are available for several languages, and they look and behave the same for the most part across all these languages. Development began on RxSwift in February 2015, and it became the official reactive extension for Swift in August the same year.
As mentioned earlier, all of the Rx extensions look and behave the same way, and that’s because each language implements the same patterns and operators in a language-specific idiomatic way.
There are also platform-specific extensions such as Cocoa and CocoaTouch, which means that you can develop in the language and platform of your choice, which will be the native Swift platform in this case. The Rx skills are universal and portable to other languages and platforms, which means that teams working with cross-platforms are able to better coordinate using reactive extensions.
To deliver a unique user experience, they are unique to each platform while ensuring that common behaviors and business rules are consistently implemented across all platforms.
Rx offers a common set of operators: To perform various tasks based on events in an asynchronous fashion, Rx typically implements two common patterns:
- A subject maintains a list of dependents called Observers, and it notifies them of changes. NotificationCenter and KVO are also implementations of this observer pattern.
- Rx also implements the iterator pattern, which is how sequences can be traversed. If you have spent time with Swift, then you will have definitely worked with the iterator pattern while trying to traverse over sequences and collections. We are talking about the same things here and in fact, in Rx, Everything is a Sequence:
Pretty much everything is a sequence of data or events or something that operates on observable sequences.
An observable sequence is just a sequence. The key advantage of an observable sequence over Swift’s standard sequence type is that it can also operate asynchronously.
Lifecycle of an observable:
An observable sequence can emit things known as events. It can emit zero or more events; when a value or collection of values is added to or put onto a sequence, it will send a next event containing that value or collection to its observers. This is known as emitting, and the values are referred to as elements. This works the same way as observable sequences of events such as taps or other recognized gestures.
If an error is encountered, a sequence can emit an error event containing an error type; this will terminate the sequence.
A sequence can also terminate normally and in doing so it will emit a completed event.
If at any point you want to stop observing a sequence, you can cancel the subscription by calling dispose() on it; this is like removing an observer in KVO or the NotificationCenter API, or you can also just add subscriptions to an instance of DisposeBag, which will take care of properly canceling subscriptions on deinit() for you.
Subject is a special type in RxSwift that can act as both of these:
- An Observable sequence, which means it can be subscribed to
- An Observer that enables adding new elements onto a subject that will then be emitted to the subject subscribers
There are four subject types in RxSwift, each with unique characteristics that you may find useful in different scenarios. They are as listed:
Let’s discuss each one of these subjects in turn.
A PublishSubject emits only new next events to its subscribers; in other words, elements added to a PublishSubject before a subscriber subscribes will not be received by that subscriber. This concept of emitting previous next events to new subscribers is called replaying, and publish subjects DO NOT replay. If terminated, though, a publish subject will re-emit its stop event, that is, a completed or error event to its new subscribers; actually, all subjects will re-emit stop events to new subscribers.
If you are creating a real-time indicator, you won’t care what the last element was before a new subscription, you only want new elements coming in after the fact. PublishSubject will be useful in that scenario.
Sometimes you want new subscribers to always receive the most recent next event in the sequence even if they subscribed after that event was emitted; for this, you can use a BehaviorSubject. A BehaviorSubject is initialised with a starting value, and then it replays to the new subscribers a next event containing the most recent elements or the initial value if no new recent elements have been added to it beforehand. Remember that if any subject type is terminated, it will reemit its stop event, whether that is a completed or a new event to its subscribers. For example, in a chat app, you might use a BehaviorSubject to pre-fill a new posts tile text field beginning with the initial name untitled.
If you want to replay more than the most recent element on a sequence to new subscribers, you use a ReplaySubject. A ReplaySubject is initialised with a buffer size and that value cannot be changed after initialisation. It will maintain a buffer up to the buffer size of the most recent next events, and it will replay the buffer to the new subscribers as if those events had happened immediately after each other, right after subscribing. It will also reemit its stop event to new subscribers. You can use replay subject to display as many as the five most recent search items whenever a search controller is presented.
A variable is essentially a wrapper around BehaviorSubject. It will replay a next event with the latest or initial value to new subscribers. A variable is guaranteed to never emit an error event and terminate. It also automatically completes when its about to be deallocated, which makes this subject unique. Unlike other subject types, a variable uses the dot “.” syntax to get the latest value or to set a new value onto it. You can access a variable’s BehaviorSubject by calling as Observable() on it.
We will cover all these subjects in my book titled: Reactive programming With Swift 4.
As discussed earlier, we know that Rx is all about working with Observable sequences; the way you work with them is by using one or more operators on them. Operators are methods of the various observable types as discussed earlier.
Marble diagrams are interactive diagrams that depict how Rx operators work with observable sequences.
Consider this example:
You can visit RxMarbles.com to view and play around with marble diagrams for most Rx operators that you will find in RxSwift. For instance, from the preceding marble diagram, you can see how a map operator works.
The elements in the top line represent elements emitted from an observable sequence and the elements in the bottom line represent the outcome sequence, that is, after invoking the operator with whatever parameters are specified on the original sequence or multiple sequences in some cases.
Here in map operator in the marble diagram, there are three elements emitted from the observable sequence. The map operator is used to map the operation specified inside the map operation and apply it to each element of the observable sequence as shown by the top line. So, in this case, the map operator will take each element from the observable sequence and produce a square of each value to generate a new sequence, as shown by the bottom line.
The vertical line at the end indicates that a sequence has terminated normally via a completed event.
An observable and the operators you use on it will execute and notify observers on the same thread on which the subscribe operator is being used.
Schedulers are an abstraction that let you specify distinct queues on which to perform work. It is always a good idea to move all the intensive work off the main thread to keep it as responsive as possible for users, and schedulers make it easy to do this. They can be Serial or Concurrent.
You will use a serial scheduler if you want to ensure that work is carried out on that queue in the order it was added to the queue, otherwise you will typically just use a concurrent queue.
There are built-in scheduler types that work with Grand Central Dispatch (GCD) queues and NSOperationQueues. These are as listed:
- SerialDispatchQueueScheduler, which will dispatch work on a specified serial dispatch queue
- ConcurrentDispatchQueueScheduler, which will dispatch work on a concurrent dispatch queue
- OperationQueueScheduler, which will perform the work on a specified NSOperations queue
You can also create your own custom schedulers by conforming to the ImmediateScheduler protocol.
You also have access to a couple of helpful singletons that can be useful for common needs, such as the following:
CurrentThreadScheduler, which represents the current thread and is the default scheduler, and MainScheduler, which represents the main thread typically used for all UI-related work.
To specify a scheduler on which a subscription is created, it’s handled or executed and is disposed of using the suscribeOn operator. This operator is not used normally, instead observeOn is used most of the time; it specifies the scheduler about the next, error, and completed events. The important thing to remember is that subscribeOn will direct which subscription is running on which scheduler, whereas observeOn only directs which scheduler to use after or below where observeOn is used in a chain.
In the next blog I will talk about when should you consider using Rx while creating IOS apps, till then stay tuned and enjoy reading:)
To have a solid grasp over reactive concepts and write IOS applications in RxSwift you can find the link to my book Reactive programming in Swift 4
Thanks for reading, please share it if you found it useful :)