RxSwift Made Easy: Part 2 — Working with Subjects

Tim Beals 🎸
Swift2Go
Published in
6 min readAug 27, 2018

In the first part of this series, we created an observable sequence, subscribed to it and observed the events that were emitted. With very few lines of code we were able to see the nuts and bolts of reactive programming, which all advanced concepts are built on. To read part one, click here.

What are Subjects?

The prevalence of Subjects in reactive programming cannot be overstated. If you are working in a reactive paradigm, the chances are you will be using Subjects. A lot. A Subject is a reactive type that is both an Observable Sequence and an Observer. What this means is that subjects can accept subscriptions and emit events (like a typical Observable), as well as add new elements onto the sequence. This stateful holding of observers makes Subjects extremely powerful.

There are four subject types: PublishSubject, BehaviorSubject, Variable, ReplaySubject. In the next section, we will describe these types, highlight their differences and look at a coding example for each one.

Two terms are essential for understanding the differences between the different subject types, so let’s take a quick look at them before going further:

Replay — In some cases you may want a new subscriber to receive the most recent next() event(s) from the sequence it is subscribing to. Even though the event(s) have already been emitted, the subject can store them and emit them again to a single subscriber at the moment of subscription. This is called replaying.

Stop Event — This is an event that terminates a sequence, either completed()or error()

PublishSubject

A PublishSubject is concerned only with emitting new events to its subscribers. It does not replay next() events, so any that existed before the subscription will not be received by that subscriber. It does re-emit stop events to new subscribers however. This means that if you subscribe to a sequence that has already been terminated, the subscriber will receive that information. It is worth noting that all subject types re-emit stop events.

A good use case for a PublishSubject is any sort of ticker. If you want a live feed of information, you likely don’t want to receive any historical events. This makes the PublishSubject ideal for your purpose.

In this simple example you can see the events that are received by a subscription to a PublishSubject:

Notice in this example that event “next 1” (line 9) is emitted before the subscription is created. As the PublishSubject type does not replay, “next 1” is never received by the subscription. Additionally, “next 3” is never received as it comes after the stop event, an error (line 18).

BehaviorSubject

A BehaviorSubject stores the most recent next() event, which is able to be replayed to new subscribers. In other words, a new subscriber can receive the most recent next() event even if they subscribe after the event was emitted. A BehaviorSubject must not have an empty buffer, so it is initialized with a starting value which acts as the initial next() event. This value gets overwritten as soon as a new element is added to the sequence. Like all Subject types, the BehaviorSubject re-emits stop events to new subscribers.

A good use case for BehaviorSubject might any property with a placeholder or default value. In a game, you may start with a placeholder text like “Player 1” which would be the starting value that the BehaviorSubject is initialized with. When the user changes the value in the textfield the BehaviorSubject emits the new value to any observers as a next() event and stores it in the buffer to be replayed if and when a new observer is eventually added.

In this example you can see the basic setup and subscription to a BehaviorSubject.

Notice in this example that the BehaviorSubject is instantiated with a starting value (line 3). When the first subscription (Sub A) is added, the Behavior subject replays the starting value (lines 5–8). The event “next 1” is then emitted and received by Sub A and the value stored in the BehaviorSubject for replay (line 10). When the second subscription (Sub B) is created, the BehaviorSubject replays “next 1” (lines 12–14). The final events “next 2” and “completed” are emitted to both Sub A and Sub B. The sequence terminates on the completed event (line 19).

Variable

At it’s heart, Variable is a wrapper around BehaviorSubject that allows for simpler handling. Instead of the usual sending of next() events, Variable provides dot syntax for getting and setting a single value that is both emitted as a next() event and stored for replay. The exposed .value property gets and sets the value to a privately stored property _value. Additionally, it creates a new next() event for the privately held BehaviorSubject contained in the Variable in the setter method. Variable also has a method .asObservable()which returns the privately held BehaviorSubject in order to manage its subscribers.

Variables do not allow for early termination. In other words, you cannot send an error() or completed() event to terminate the sequence. You simply wait for the Variable to be deallocated and terminate the sequence itself in its deinit method.

Variable is planned for deprecation so keep your eye out for a similar object in the near future.

This example should look very familiar as it has exactly the same operations as the previous BehaviorSubject example. In this case, note that the Variable is instantiated with a starting value (line 3). The .asObservable() method is used to add subscriptions (lines 5 & 12), and each time the most recent value is replayed. The .value property is used to send next() events and update the value for replay (lines 10 & 17), and while no completed event is explicitly sent, the Variable handles this as the execution ends and it is deallocated.

ReplaySubject

So far we have seen a subject with no replay (PublishSubject) and two subjects that replay a single next event (BehaviorSubject and Variable). As its name suggests, the ReplaySubject provides you the ability to replay many next events. In order to do this, you specify your buffer size when you instantiate the ReplaySubject, and it maintains your latest next events up to the buffer limit. When a new subscriber is added, the events stored in the buffer are replayed one after the other as if they are occurring in rapid succession immediately after subscription. Once again, stop events are re-emitted to new subscribers.

Good use cases for a Replay subject are those recording historical information such as storing most recent search terms or operations that a user might want to undo.

In this example, the ReplaySubject holds a buffer of 4 next events of type String (line 3). Immediately, 5 next events occur before the subscription is added. As we would expect, this overflows the buffer meaning that “(pre) Event 1" will never be received (confirmed in the console print out). Like we have seen in previous examples, the stop event (line 18) terminates the sequence meaning future next events (line 20) cannot be received by observers.

Wrap Up

Hopefully you can begin to see why Subjects are so powerful in RxSwift. By deciding the extent to which you want to replay events, you can simply and efficiently allocate memory and access only the historical events that you need.

Click here for part 3: Transforming Observable Sequences

--

--