• Jennifer Eve Vega

RxSwift: Subjects

Updated: Mar 19



A Subject acts as an observable and as an observer.


Types of Subjects in RxSwift:


1. PublishSubject

Initialized without any initial value

let subject = PublishSubject<String>() 
//`subject` emitted a string value, but because there were no subscribers yet, it will not be printed
subject.onNext("Not printed because there are no subscribers yet.")

subject.subscribe { (value) in
    debugPrint("value: ", value)
}.disposed(by: bag)

subject.onNext("How about now?")

subject.onNext("") was called twice, but, only the last string will be printed on our console because when the first onNext() was called, there were no subscribers then.

"value: " next(How about now?)


2. BehaviorSubject

Initialized with an initial value. This value (or the latest value) will be replayed to the new subscribers.

let bSubject = BehaviorSubject<String>(value: "Hello World!")
bSubject.subscribe { value in
      debugPrint("value: ", value)
}.disposed(by: bag)

For BehaviorSubject, we initialized it with an initial value, which will be replayed to its subscribers. And in the example above, there were new values emitted, so the moment there's a subscriber, the initial value will be printed on our console.

"value: " next(Hello World!)

So, if a new value was emitted before there was a subscriber, then the last value will be replayed to the subscribers.

let bSubject = BehaviorSubject<String>(value: "Hello World!")
bSubject.onNext("This is not the initial value")
bSubject.subscribe { value in
     debugPrint("value: ", value)
}.disposed(by: bag)
"value: " next(This is not the initial value)

In the last two examples, bSubject is subscribing to its own events. But we can also have other subscribers.

let bSubject = BehaviorSubject<String>(value: "Hello World!")
let otherSubscriber = bSubject.subscribe { (value) in
                          debugPrint("other subs value: ", value)
                      }
bSubject.onNext("Is anyone listening to this bsubject?")

Can you guess what you'll see when you run this code?

"other subs value: " next(Hello World!)
"other subs value: " next(Is anyone listening to this bsubject?)

Now, what will happen if we dispose otherSubscriber and a new value was emitted?

otherSubscriber.dispose()
bSubject.onNext("new value after disposing other subscriber")

The last value will not be printed.


What if the bSubject is completed?

bSubject.onCompleted()

Then any new subscribers after it was completed will only receive the onComplete event.

let aNewSubscriber = bSubject.subscribe { (value) in
                            debugPrint("third subscriber after completing observable: ", value)
                     } onCompleted: {
                            debugPrint("third subscriber completed:")
                     }.disposed(by: bag)

Console:

"third subscriber completed:"


3. RelaySubject

Initialized with a buffer size and will maintain this buffer size (with elements) and it latest values will be replayed to the subscribers.

let rSubject = ReplaySubject<String>.create(bufferSize: 3)
rSubject.onNext("1")
rSubject.onNext("2")
rSubject.onNext("3")
rSubject.onNext("4")
rSubject.onNext("5")
rSubject.subscribe { (value) in
            debugPrint("value1: ", value)
        }.disposed(by: bag)

Since this replay subject only has a buffer of 3 elements, then the last 3 elements will be replayed to the subscriber. Which means in this example, we will only see "3", "4" and "5" printed.

"value1: " next(3)
"value1: " next(4)
"value1: " next(5)

Let's emit more values:

rSubject.onNext("6")
rSubject.onNext("7")
rSubject.onNext("8")

And let's have a new subscriber to rSubject. What will be printed?

let newSubs = rSubject.subscribe { (value) in
                           debugPrint("newSubs: ", value)
                      }.disposed(by: bag)

Since the relaySubject can keep the last 3 elements (our buffer size), then, we can expect that newSubs will print:

"newSubs: " next(6)
"newSubs: " next(7)
"newSubs: " next(8)


4. AsyncSubject

It will emit the last event after it received a completed event.

let aSubject = AsyncSubject<String>()
aSubject.subscribe { (value) in
             debugPrint("value: ", value)
       }.disposed(by: bag)
            
aSubject.onNext("1")
aSubject.onNext("2")
aSubject.onNext("3")

Given the example above, what is printed on our console?


Nothing.


Because aSubject has not received a completed event, yet. So nothing is printed. Now, if you add the completed event after the last line:

aSubject.onCompleted()

Then, you will see the last event printed on our console:

"value: " next(3)
"value: " completed

25 views0 comments

Recent Posts

See All