Cover

Building on the previous article, this article will introduce the Observables part of RxSwift.

An Observable in RxSwift is also called an Observable Sequence, Sequence, or Stream. An Observable emits events asynchronously to form an event stream, and data propagates along the event stream. Here is a graphical representation of the event flow:

The 2017-09-07-01

The arrow from left to right represents the timeline, while the three circles form an observable sequence. And it’s going to go from left to right. In addition, events can be fired at any time during the life of the observable sequence.

Observable Life Cycle

The three circles in the figure above are actually next events in RxSwift. In addition to Next, there are completed and error events in RxSwift, both of which represent summaries of the life cycle of the event flow.

Normal termination as indicated by completed:

The 2017-09-07-02

The end of an exception as indicated by error:

The 2017-09-07-03

In the source code, these three types of events are defined as follows:

/// Represents a sequence event.
///
/// Sequence grammar: 
/// **next\* (error | completed)**
public enum Event<Element> {
    /// Next element is produced.
    case next(Element)

    /// Sequence terminated with an error.
    case error(Swift.Error)

    /// Sequence completed successfully.
    case completed
}Copy the code

In the code, we can clearly see that the Next event is propagated with an instance, the ERROR event is propagated with an instance of error, and completed carries nothing.

New observables

Of all Observable creation methods, the simplest is just:

/ / 1
let one = 1 
let two = 2 
let three = 3

/ / 2
let observable: Observable<Int> = Observable<Int>.just(one)Copy the code

An Observable created as the class method just contains only one element. But most of the time, an Observable contains more than one element during its life, and creation is simple:

let observable2 = Observable.of(one, two, three)Copy the code

Perhaps the above code gives a misleading impression that the data in Observable2 might be an array type. Fortunately, we can verify this with the following code:

observable2.subscribe(onNext: { element in
        print(element)    
 })

/* Print the result: 1 2 3 */Copy the code

To actually create a variable of array type:

let observable3 = Observable.of([one, two, three])
observable3.subscribe(onNext: { element in
        print(element)    
 })

/* Print result: [1, 2, 3] */Copy the code

The above are just a few common Observable creation methods, but more can be found in the documentation and code.

Subscribe to observables

Notifications are one of the most frequently used design patterns in everyday iOS programming. We broadcast and subscribe messages through NotificationCenter. Here is a typical notification mode code for handling UIKeyboardDidChangeFrame messages:

let observer = NotificationCenter.default.addObserver( 
forName: .UIKeyboardDidChangeFrame, 
object: nil, 
queue: nil ) {  notification in 
    / / closures
}Copy the code

The subscribe operation in RxSwift is also very simple, simply calling the SUBSCRIBE method. Unlike NotificationCenter, however, in RxSwift each subscription is unique and there is no global singleton like Default.

More importantly, an Observable doesn’t send notifications when there are no subscribers. In addition, an Observable is actually a sequence, so subscribes are a bit like calling Iterator next from the Swift standard library over and over again:

let sequence = 0..<3

var iterator = sequence.makeIterator()

while let n = iterator.next() { 
    print(n)
}

/* Print the result: 0 1 2 */Copy the code

However, the RxSwift subscription operation is obviously more straightforward than this, and can handle the next, error, and Completed events all at once. An example of a simple subscription operation:

let one = 1 
let two = 2 
let three = 3

let observable = Observable.of(one, two, three)

observable.subscribe { event in 
    print(event) 
}Copy the code

The subscribe operation is very simple: print out all the events in the observable declaration cycle. In the normal case, the result is as follows:

next(1) 
next(2) 
next(3) 
completedCopy the code

Of course, sometimes we just need data that an Observable emits:

observable.subscribe { event in
    if let element = event.element { 
        print(element)
    }
}

/* Print the result: 1 2 3 */Copy the code

Or, we need to treat different events differently:

observable .subscribe(
    onNext: { element in 
        print(element) 
    },
    onCompleted: { 
        print("Completed")})Copy the code

Unsubscribe and eliminate memory leaks

An Observable sends data only if there is a subscription and ends its life when an error or completed event is raised. However, sometimes you need to manually unsubscribe and prematurely terminate an Observable.

let observable = Observable.of("A"."B"."C")

let subscription = observable.subscribe { event in
    print(event)
}Copy the code

The code above is very simple and I’m not going to go into detail here, but let’s go straight to unsubscribe. In fact, unsubscribe is as simple as one line of code:

subscription.dispose()Copy the code

Of course, manually cancelling each subscription object is a tedious task. So RxSwift provides you with a much simpler solution. Simply by calling.AddDisposableto () at subscription time to add an object of type DisposeBag, we can cancel all cancellation actions bound to the subscription object when the DisposeBag object is destroyed.

let disposeBag = DisposeBag(a)Observable.of("A"."B"."C")
    .subscribe { 
        3 print($0)
    } 
    .addDisposableTo(disposeBag)Copy the code

The reason why we need to bind DisposeBag or manually call Dispose () to unsubscribe is that the Observable will leak memory at the end of its life if we don’t do this.

conclusion

This article only briefly introduces some of the common basics of Observables. There are many more things that are not covered here, such as subscriptions to empty and never, custom Observables using Create, and custom implementations of Error. If you are interested, I highly recommend you check out the official documentation and code.

The original address