Getting Started

This project tries to be consistent with ReactiveX.io. The general cross platform documentation and tutorials should also be valid in case of RxSwift.

  1. Observables aka Sequences
  2. Disposing
  3. Implicit Observable guarantees
  4. Creating your first Observable (aka observable sequence)
  5. Creating an Observable that performs work
  6. Sharing subscription and shareReplay operator
  7. Operators
  8. Playgrounds
  9. Custom operators
  10. Error handling
  11. Debugging Compile Errors
  12. Debugging
  13. Debugging memory leaks
  14. KVO
  15. UI layer tips
  16. Making HTTP requests
  17. RxDataSources
  18. Driver
  19. Examples

Observables aka Sequences

Basics

The equivalence of observer pattern (Observable<Element> sequence) and normal sequences (SequenceType) is the most important thing to understand about Rx.

Every Observable sequence is just a sequence. The key advantage for an Observable vs Swift’s SequenceType is that it can also receive elements asynchronously. This is the kernel of the RxSwift, documentation from here is about ways that we expand on that idea.

  • Observable(ObservableType) is equivalent to SequenceType
  • ObservableType.subscribe method is equivalent to SequenceType.generate method.
  • Observer (callback) needs to be passed to ObservableType.subscribe method to receive sequence elements instead of calling next() on the returned generator.

Sequences are a simple, familiar concept that is easy to visualize.

People are creatures with huge visual cortexes. When we can visualize a concept easily, it’s a lot easier to reason about it.

We can lift a lot of the cognitive load from trying to simulate event state machines inside every Rx operator onto high level operations over sequences.

If we don’t use Rx but model asynchronous systems, that probably means that our code is full of state machines and transient states that we need to simulate instead of abstracting away.

Lists and sequences are probably one of the first concepts mathematicians and programmers learn.

Here is a sequence of numbers:

  1. --1--2--3--4--5--6--| // terminates normally

Another sequence, with characters:

  1. --a--b--a--a--a---d---X // terminates with error

Some sequences are finite and others are infinite, like a sequence of button taps:

  1. ---tap-tap-------tap--->

These are called marble diagrams. There are more marble diagrams at rxmarbles.com.

If we were to specify sequence grammar as a regular expression it would look like:

Next* (Error | Completed)?

This describes the following:

  • Sequences can have 0 or more elements.
  • Once an Error or Completed event is received, the sequence cannot produce any other element.

Sequences in Rx are described by a push interface (aka callback).

  1. enum Event<Element> {
  2. case Next(Element) // next element of a sequence
  3. case Error(ErrorType) // sequence failed with error
  4. case Completed // sequence terminated successfully
  5. }
  6. class Observable<Element> {
  7. func subscribe(observer: Observer<Element>) -> Disposable
  8. }
  9. protocol ObserverType {
  10. func on(event: Event<Element>)
  11. }

When a sequence sends the Completed or Error event all internal resources that compute sequence elements will be freed.

To cancel production of sequence elements and free resources immediately, call dispose on the returned subscription.

If a sequence terminates in finite time, not calling dispose or not using addDisposableTo(disposeBag) won’t cause any permanent resource leaks. However, those resources will be used until the sequence completes, either by finishing production of elements or returning an error.

If a sequence does not terminate in some way, resources will be allocated permanently unless dispose is called manually, automatically inside of a disposeBag, takeUntil or in some other way.

Using dispose bags or takeUntil operator is a robust way of making sure resources are cleaned up. We recommend using them in production even if the sequences will terminate in finite time.

In case you are curious why ErrorType isn’t generic, you can find explanation here.

Disposing

There is one additional way an observed sequence can terminate. When we are done with a sequence and we want to release all of the resources allocated to compute the upcoming elements, we can call dispose on a subscription.

Here is an example with the interval operator.

  1. let subscription = Observable<Int>.interval(0.3, scheduler: scheduler)
  2. .subscribe { event in
  3. print(event)
  4. }
  5. NSThread.sleepForTimeInterval(2)
  6. subscription.dispose()

This will print:

  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5

Note the you usually do not want to manually call dispose; this is only educational example. Calling dispose manually is usually a bad code smell. There are better ways to dispose subscriptions. We can use DisposeBag, the takeUntil operator, or some other mechanism.

So can this code print something after the dispose call executed? The answer is: it depends.

  • If the scheduler is a serial scheduler (ex. MainScheduler) and dispose is called on on the same serial scheduler, the answer is no.

  • Otherwise it is yes.

You can find out more about schedulers here.

You simply have two processes happening in parallel.

  • one is producing elements
  • the other is disposing the subscription

The question “Can something be printed after?” does not even make sense in the case that those processes are on different schedulers.

A few more examples just to be sure (observeOn is explained here).

In case we have something like:

  1. let subscription = Observable<Int>.interval(0.3, scheduler: scheduler)
  2. .observeOn(MainScheduler.instance)
  3. .subscribe { event in
  4. print(event)
  5. }
  6. // ....
  7. subscription.dispose() // called from main thread

After the dispose call returns, nothing will be printed. That is guaranteed.

Also, in this case:

  1. let subscription = Observable<Int>.interval(0.3, scheduler: scheduler)
  2. .observeOn(serialScheduler)
  3. .subscribe { event in
  4. print(event)
  5. }
  6. // ...
  7. subscription.dispose() // executing on same `serialScheduler`

After the dispose call returns, nothing will be printed. That is guaranteed.

Dispose Bags

Dispose bags are used to return ARC like behavior to RX.

When a DisposeBag is deallocated, it will call dispose on each of the added disposables.

It does not have a dispose method and therefore does not allow calling explicit dispose on purpose. If immediate cleanup is required, we can just create a new bag.

  1. self.disposeBag = DisposeBag()

This will clear old references and cause disposal of resources.

If that explicit manual disposal is still wanted, use CompositeDisposable. It has the wanted behavior but once that dispose method is called, it will immediately dispose any newly added disposable.

Take until

Additional way to automatically dispose subscription on dealloc is to use takeUntil operator.

  1. sequence
  2. .takeUntil(self.rx_deallocated)
  3. .subscribe {
  4. print($0)
  5. }

Implicit Observable guarantees

There is also a couple of additional guarantees that all sequence producers (Observables) must honor.

It doesn’t matter on which thread they produce elements, but if they generate one element and send it to the observer observer.on(.Next(nextElement)), they can’t send next element until observer.on method has finished execution.

Producers also cannot send terminating .Completed or .Error in case .Next event hasn’t finished.

In short, consider this example:

  1. someObservable
  2. .subscribe { (e: Event<Element>) in
  3. print("Event processing started")
  4. // processing
  5. print("Event processing ended")
  6. }

this will always print:

  1. Event processing started
  2. Event processing ended
  3. Event processing started
  4. Event processing ended
  5. Event processing started
  6. Event processing ended

it can never print:

  1. Event processing started
  2. Event processing started
  3. Event processing ended
  4. Event processing ended

Creating your own Observable (aka observable sequence)

There is one crucial thing to understand about observables.

When an observable is created, it doesn’t perform any work simply because it has been created.

It is true that Observable can generate elements in many ways. Some of them cause side effects and some of them tap into existing running processes like tapping into mouse events, etc.

But if you just call a method that returns an Observable, no sequence generation is performed, and there are no side effects. Observable is just a definition how the sequence is generated and what parameters are used for element generation. Sequence generation starts when subscribe method is called.

E.g. Let’s say you have a method with similar prototype:

  1. func searchWikipedia(searchTerm: String) -> Observable<Results> {}
  1. let searchForMe = searchWikipedia("me")
  2. // no requests are performed, no work is being done, no URL requests were fired
  3. let cancel = searchForMe
  4. // sequence generation starts now, URL requests are fired
  5. .subscribeNext { results in
  6. print(results)
  7. }

There are a lot of ways how you can create your own Observable sequence. Probably the easiest way is using create function.

Let’s create a function which creates a sequence that returns one element upon subscription. That function is called ‘just’.

This is the actual implementation

  1. func myJust<E>(element: E) -> Observable<E> {
  2. return Observable.create { observer in
  3. observer.on(.Next(element))
  4. observer.on(.Completed)
  5. return NopDisposable.instance
  6. }
  7. }
  8. myJust(0)
  9. .subscribeNext { n in
  10. print(n)
  11. }

this will print:

  1. 0

Not bad. So what is the create function?

It’s just a convenience method that enables you to easily implement subscribe method using Swift closures. Like subscribe method it takes one argument, observer, and returns disposable.

Sequence implemented this way is actually synchronous. It will generate elements and terminate before subscribe call returns disposable representing subscription. Because of that it doesn’t really matter what disposable it returns, process of generating elements can’t be interrupted.

When generating synchronous sequences, the usual disposable to return is singleton instance of NopDisposable.

Lets now create an observable that returns elements from an array.

This is the actual implementation

  1. func myFrom<E>(sequence: [E]) -> Observable<E> {
  2. return Observable.create { observer in
  3. for element in sequence {
  4. observer.on(.Next(element))
  5. }
  6. observer.on(.Completed)
  7. return NopDisposable.instance
  8. }
  9. }
  10. let stringCounter = myFrom(["first", "second"])
  11. print("Started ----")
  12. // first time
  13. stringCounter
  14. .subscribeNext { n in
  15. print(n)
  16. }
  17. print("----")
  18. // again
  19. stringCounter
  20. .subscribeNext { n in
  21. print(n)
  22. }
  23. print("Ended ----")

This will print:

  1. Started ----
  2. first
  3. second
  4. ----
  5. first
  6. second
  7. Ended ----

Creating an Observable that performs work

Ok, now something more interesting. Let’s create that interval operator that was used in previous examples.

This is equivalent of actual implementation for dispatch queue schedulers

  1. func myInterval(interval: NSTimeInterval) -> Observable<Int> {
  2. return Observable.create { observer in
  3. print("Subscribed")
  4. let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
  5. let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
  6. var next = 0
  7. dispatch_source_set_timer(timer, 0, UInt64(interval * Double(NSEC_PER_SEC)), 0)
  8. let cancel = AnonymousDisposable {
  9. print("Disposed")
  10. dispatch_source_cancel(timer)
  11. }
  12. dispatch_source_set_event_handler(timer, {
  13. if cancel.disposed {
  14. return
  15. }
  16. observer.on(.Next(next))
  17. next += 1
  18. })
  19. dispatch_resume(timer)
  20. return cancel
  21. }
  22. }
  1. let counter = myInterval(0.1)
  2. print("Started ----")
  3. let subscription = counter
  4. .subscribeNext { n in
  5. print(n)
  6. }
  7. NSThread.sleepForTimeInterval(0.5)
  8. subscription.dispose()
  9. print("Ended ----")

This will print

  1. Started ----
  2. Subscribed
  3. 0
  4. 1
  5. 2
  6. 3
  7. 4
  8. Disposed
  9. Ended ----

What if you would write

  1. let counter = myInterval(0.1)
  2. print("Started ----")
  3. let subscription1 = counter
  4. .subscribeNext { n in
  5. print("First \(n)")
  6. }
  7. let subscription2 = counter
  8. .subscribeNext { n in
  9. print("Second \(n)")
  10. }
  11. NSThread.sleepForTimeInterval(0.5)
  12. subscription1.dispose()
  13. NSThread.sleepForTimeInterval(0.5)
  14. subscription2.dispose()
  15. print("Ended ----")

this would print:

  1. Started ----
  2. Subscribed
  3. Subscribed
  4. First 0
  5. Second 0
  6. First 1
  7. Second 1
  8. First 2
  9. Second 2
  10. First 3
  11. Second 3
  12. First 4
  13. Second 4
  14. Disposed
  15. Second 5
  16. Second 6
  17. Second 7
  18. Second 8
  19. Second 9
  20. Disposed
  21. Ended ----

Every subscriber upon subscription usually generates it’s own separate sequence of elements. Operators are stateless by default. There are vastly more stateless operators than stateful ones.

Sharing subscription and shareReplay operator

But what if you want that multiple observers share events (elements) from only one subscription?

There are two things that need to be defined.

  • How to handle past elements that have been received before the new subscriber was interested in observing them (replay latest only, replay all, replay last n)
  • How to decide when to fire that shared subscription (refCount, manual or some other algorithm)

The usual choice is a combination of replay(1).refCount() aka shareReplay().

  1. let counter = myInterval(0.1)
  2. .shareReplay(1)
  3. print("Started ----")
  4. let subscription1 = counter
  5. .subscribeNext { n in
  6. print("First \(n)")
  7. }
  8. let subscription2 = counter
  9. .subscribeNext { n in
  10. print("Second \(n)")
  11. }
  12. NSThread.sleepForTimeInterval(0.5)
  13. subscription1.dispose()
  14. NSThread.sleepForTimeInterval(0.5)
  15. subscription2.dispose()
  16. print("Ended ----")

this will print

  1. Started ----
  2. Subscribed
  3. First 0
  4. Second 0
  5. First 1
  6. Second 1
  7. First 2
  8. Second 2
  9. First 3
  10. Second 3
  11. First 4
  12. Second 4
  13. First 5
  14. Second 5
  15. Second 6
  16. Second 7
  17. Second 8
  18. Second 9
  19. Disposed
  20. Ended ----

Notice how now there is only one Subscribed and Disposed event.

Behavior for URL observables is equivalent.

This is how HTTP requests are wrapped in Rx. It’s pretty much the same pattern like the interval operator.

  1. extension NSURLSession {
  2. public func rx_response(request: NSURLRequest) -> Observable<(NSData, NSURLResponse)> {
  3. return Observable.create { observer in
  4. let task = self.dataTaskWithRequest(request) { (data, response, error) in
  5. guard let response = response, data = data else {
  6. observer.on(.Error(error ?? RxCocoaURLError.Unknown))
  7. return
  8. }
  9. guard let httpResponse = response as? NSHTTPURLResponse else {
  10. observer.on(.Error(RxCocoaURLError.NonHTTPResponse(response: response)))
  11. return
  12. }
  13. observer.on(.Next(data, httpResponse))
  14. observer.on(.Completed)
  15. }
  16. task.resume()
  17. return AnonymousDisposable {
  18. task.cancel()
  19. }
  20. }
  21. }
  22. }

Operators

There are numerous operators implemented in RxSwift. The complete list can be found here.

Marble diagrams for all operators can be found on ReactiveX.io

Almost all operators are demonstrated in Playgrounds.

To use playgrounds please open Rx.xcworkspace, build RxSwift-OSX scheme and then open playgrounds in Rx.xcworkspace tree view.

In case you need an operator, and don’t know how to find it there a decision tree of operators.

Supported RxSwift operators are also grouped by function they perform, so that can also help.

Custom operators

There are two ways how you can create custom operators.

Easy way

All of the internal code uses highly optimized versions of operators, so they aren’t the best tutorial material. That’s why it’s highly encouraged to use standard operators.

Fortunately there is an easier way to create operators. Creating new operators is actually all about creating observables, and previous chapter already describes how to do that.

Lets see how an unoptimized map operator can be implemented.

  1. extension ObservableType {
  2. func myMap<R>(transform: E -> R) -> Observable<R> {
  3. return Observable.create { observer in
  4. let subscription = self.subscribe { e in
  5. switch e {
  6. case .Next(let value):
  7. let result = transform(value)
  8. observer.on(.Next(result))
  9. case .Error(let error):
  10. observer.on(.Error(error))
  11. case .Completed:
  12. observer.on(.Completed)
  13. }
  14. }
  15. return subscription
  16. }
  17. }
  18. }

So now you can use your own map:

  1. let subscription = myInterval(0.1)
  2. .myMap { e in
  3. return "This is simply \(e)"
  4. }
  5. .subscribeNext { n in
  6. print(n)
  7. }

and this will print

  1. Subscribed
  2. This is simply 0
  3. This is simply 1
  4. This is simply 2
  5. This is simply 3
  6. This is simply 4
  7. This is simply 5
  8. This is simply 6
  9. This is simply 7
  10. This is simply 8
  11. ...

Life happens

So what if it’s just too hard to solve some cases with custom operators? You can exit the Rx monad, perform actions in imperative world, and then tunnel results to Rx again using Subjects.

This isn’t something that should be practiced often, and is a bad code smell, but you can do it.

  1. let magicBeings: Observable<MagicBeing> = summonFromMiddleEarth()
  2. magicBeings
  3. .subscribeNext { being in // exit the Rx monad
  4. self.doSomeStateMagic(being)
  5. }
  6. .addDisposableTo(disposeBag)
  7. //
  8. // Mess
  9. //
  10. let kitten = globalParty( // calculate something in messy world
  11. being,
  12. UIApplication.delegate.dataSomething.attendees
  13. )
  14. kittens.on(.Next(kitten)) // send result back to rx
  15. //
  16. // Another mess
  17. //
  18. let kittens = Variable(firstKitten) // again back in Rx monad
  19. kittens.asObservable()
  20. .map { kitten in
  21. return kitten.purr()
  22. }
  23. // ....

Every time you do this, somebody will probably write this code somewhere

  1. kittens
  2. .subscribeNext { kitten in
  3. // so something with kitten
  4. }
  5. .addDisposableTo(disposeBag)

so please try not to do this.

Playgrounds

If you are unsure how exactly some of the operators work, playgrounds contain almost all of the operators already prepared with small examples that illustrate their behavior.

To use playgrounds please open Rx.xcworkspace, build RxSwift-OSX scheme and then open playgrounds in Rx.xcworkspace tree view.

To view the results of the examples in the playgrounds, please open the Assistant Editor. You can open Assistant Editor by clicking on View > Assistant Editor > Show Assistant Editor

Error handling

The are two error mechanisms.

Asynchronous error handling mechanism in observables

Error handling is pretty straightforward. If one sequence terminates with error, then all of the dependent sequences will terminate with error. It’s usual short circuit logic.

You can recover from failure of observable by using catch operator. There are various overloads that enable you to specify recovery in great detail.

There is also retry operator that enables retries in case of errored sequence.

Debugging Compile Errors

When writing elegant RxSwift/RxCocoa code, you are probably relying heavily on compiler to deduce types of Observables. This is one of the reasons why Swift is awesome, but it can also be frustrating sometimes.

  1. images = word
  2. .filter { $0.containsString("important") }
  3. .flatMap { word in
  4. return self.api.loadFlickrFeed("karate")
  5. .catchError { error in
  6. return just(JSON(1))
  7. }
  8. }

If compiler reports that there is an error somewhere in this expression, I would suggest first annotating return types.

  1. images = word
  2. .filter { s -> Bool in s.containsString("important") }
  3. .flatMap { word -> Observable<JSON> in
  4. return self.api.loadFlickrFeed("karate")
  5. .catchError { error -> Observable<JSON> in
  6. return just(JSON(1))
  7. }
  8. }

If that doesn’t work, you can continue adding more type annotations until you’ve localized the error.

  1. images = word
  2. .filter { (s: String) -> Bool in s.containsString("important") }
  3. .flatMap { (word: String) -> Observable<JSON> in
  4. return self.api.loadFlickrFeed("karate")
  5. .catchError { (error: NSError) -> Observable<JSON> in
  6. return just(JSON(1))
  7. }
  8. }

I would suggest first annotating return types and arguments of closures.

Usually after you have fixed the error, you can remove the type annotations to clean up your code again.

Debugging

Using debugger alone is useful, but usually using debug operator will be more efficient. debug operator will print out all events to standard output and you can add also label those events.

debug acts like a probe. Here is an example of using it:

  1. let subscription = myInterval(0.1)
  2. .debug("my probe")
  3. .map { e in
  4. return "This is simply \(e)"
  5. }
  6. .subscribeNext { n in
  7. print(n)
  8. }
  9. NSThread.sleepForTimeInterval(0.5)
  10. subscription.dispose()

will print

  1. [my probe] subscribed
  2. Subscribed
  3. [my probe] -> Event Next(Box(0))
  4. This is simply 0
  5. [my probe] -> Event Next(Box(1))
  6. This is simply 1
  7. [my probe] -> Event Next(Box(2))
  8. This is simply 2
  9. [my probe] -> Event Next(Box(3))
  10. This is simply 3
  11. [my probe] -> Event Next(Box(4))
  12. This is simply 4
  13. [my probe] dispose
  14. Disposed

You can also easily create your version of the debug operator.

  1. extension ObservableType {
  2. public func myDebug(identifier: String) -> Observable<Self.E> {
  3. return Observable.create { observer in
  4. print("subscribed \(identifier)")
  5. let subscription = self.subscribe { e in
  6. print("event \(identifier) \(e)")
  7. switch e {
  8. case .Next(let value):
  9. observer.on(.Next(value))
  10. case .Error(let error):
  11. observer.on(.Error(error))
  12. case .Completed:
  13. observer.on(.Completed)
  14. }
  15. }
  16. return AnonymousDisposable {
  17. print("disposing \(identifier)")
  18. subscription.dispose()
  19. }
  20. }
  21. }
  22. }

Debugging memory leaks

In debug mode Rx tracks all allocated resources in a global variable resourceCount.

In case you want to have some resource leak detection logic, the simplest method is just printing out RxSwift.resourceCount periodically to output.

  1. /* add somewhere in
  2. func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
  3. */
  4. _ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
  5. .subscribeNext { _ in
  6. print("Resource count \(RxSwift.resourceCount)")
  7. }

Most efficient way to test for memory leaks is:

  • navigate to your screen and use it
  • navigate back
  • observe initial resource count
  • navigate second time to your screen and use it
  • navigate back
  • observe final resource count

In case there is a difference in resource count between initial and final resource counts, there might be a memory leak somewhere.

The reason why 2 navigations are suggested is because first navigation forces loading of lazy resources.

Variables

Variables represent some observable state. Variable without containing value can’t exist because initializer requires initial value.

Variable wraps a Subject. More specifically it is a BehaviorSubject. Unlike BehaviorSubject, it only exposes value interface, so variable can never terminate or fail.

It will also broadcast it’s current value immediately on subscription.

After variable is deallocated, it will complete the observable sequence returned from .asObservable().

  1. let variable = Variable(0)
  2. print("Before first subscription ---")
  3. _ = variable.asObservable()
  4. .subscribe(onNext: { n in
  5. print("First \(n)")
  6. }, onCompleted: {
  7. print("Completed 1")
  8. })
  9. print("Before send 1")
  10. variable.value = 1
  11. print("Before second subscription ---")
  12. _ = variable.asObservable()
  13. .subscribe(onNext: { n in
  14. print("Second \(n)")
  15. }, onCompleted: {
  16. print("Completed 2")
  17. })
  18. variable.value = 2
  19. print("End ---")

will print

  1. Before first subscription ---
  2. First 0
  3. Before send 1
  4. First 1
  5. Before second subscription ---
  6. Second 1
  7. First 2
  8. Second 2
  9. End ---
  10. Completed 1
  11. Completed 2

KVO

KVO is an Objective-C mechanism. That means that it wasn’t built with type safety in mind. This project tries to solve some of the problems.

There are two built in ways this library supports KVO.

  1. // KVO
  2. extension NSObject {
  3. public func rx_observe<E>(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions, retainSelf: Bool = true) -> Observable<E?> {}
  4. }
  5. #if !DISABLE_SWIZZLING
  6. // KVO
  7. extension NSObject {
  8. public func rx_observeWeakly<E>(type: E.Type, _ keyPath: String, options: NSKeyValueObservingOptions) -> Observable<E?> {}
  9. }
  10. #endif

Example how to observe frame of UIView.

WARNING: UIKit isn’t KVO compliant, but this will work.

  1. view
  2. .rx_observe(CGRect.self, "frame")
  3. .subscribeNext { frame in
  4. ...
  5. }

or

  1. view
  2. .rx_observeWeakly(CGRect.self, "frame")
  3. .subscribeNext { frame in
  4. ...
  5. }

rx_observe

rx_observe is more performant because it’s just a simple wrapper around KVO mechanism, but it has more limited usage scenarios

  • it can be used to observe paths starting from self or from ancestors in ownership graph (retainSelf = false)
  • it can be used to observe paths starting from descendants in ownership graph (retainSelf = true)
  • the paths have to consist only of strong properties, otherwise you are risking crashing the system by not unregistering KVO observer before dealloc.

E.g.

  1. self.rx_observe(CGRect.self, "view.frame", retainSelf: false)

rx_observeWeakly

rx_observeWeakly has somewhat slower than rx_observe because it has to handle object deallocation in case of weak references.

It can be used in all cases where rx_observe can be used and additionally

  • because it won’t retain observed target, it can be used to observe arbitrary object graph whose ownership relation is unknown
  • it can be used to observe weak properties

E.g.

  1. someSuspiciousViewController.rx_observeWeakly(Bool.self, "behavingOk")

Observing structs

KVO is an Objective-C mechanism so it relies heavily on NSValue.

RxCocoa has built in support for KVO observing of CGRect, CGSize and CGPoint structs.

When observing some other structures it is necessary to extract those structures from NSValue manually.

Here are examples how to extend KVO observing mechanism and rx_observe* methods for other structs by implementing KVORepresentable protocol.

UI layer tips

There are certain things that your Observables need to satisfy in the UI layer when binding to UIKit controls.

Threading

Observables need to send values on MainScheduler(UIThread). That’s just a normal UIKit/Cocoa requirement.

It is usually a good idea for you APIs to return results on MainScheduler. In case you try to bind something to UI from background thread, in Debug build RxCocoa will usually throw an exception to inform you of that.

To fix this you need to add observeOn(MainScheduler.instance).

NSURLSession extensions don’t return result on MainScheduler by default.

Errors

You can’t bind failure to UIKit controls because that is undefined behavior.

If you don’t know if Observable can fail, you can ensure it can’t fail using catchErrorJustReturn(valueThatIsReturnedWhenErrorHappens), but after an error happens the underlying sequence will still complete.

If the wanted behavior is for underlying sequence to continue producing elements, some version of retry operator is needed.

Sharing subscription

You usually want to share subscription in the UI layer. You don’t want to make separate HTTP calls to bind the same data to multiple UI elements.

Let’s say you have something like this:

  1. let searchResults = searchText
  2. .throttle(0.3, $.mainScheduler)
  3. .distinctUntilChanged
  4. .flatMapLatest { query in
  5. API.getSearchResults(query)
  6. .retry(3)
  7. .startWith([]) // clears results on new search term
  8. .catchErrorJustReturn([])
  9. }
  10. .shareReplay(1) // <- notice the `shareReplay` operator

What you usually want is to share search results once calculated. That is what shareReplay means.

It is usually a good rule of thumb in the UI layer to add shareReplay at the end of transformation chain because you really want to share calculated results. You don’t want to fire separate HTTP connections when binding searchResults to multiple UI elements.

Also take a look at Driver unit. It is designed to transparently wrap those shareReply calls, make sure elements are observed on main UI thread and that no error can be bound to UI.

Making HTTP requests

Making http requests is one of the first things people try.

You first need to build NSURLRequest object that represents the work that needs to be done.

Request determines is it a GET request, or a POST request, what is the request body, query parameters …

This is how you can create a simple GET request

  1. let request = NSURLRequest(URL: NSURL(string: "http://en.wikipedia.org/w/api.php?action=parse&page=Pizza&format=json")!)

If you want to just execute that request outside of composition with other observables, this is what needs to be done.

  1. let responseJSON = NSURLSession.sharedSession().rx_JSON(request)
  2. // no requests will be performed up to this point
  3. // `responseJSON` is just a description how to fetch the response
  4. let cancelRequest = responseJSON
  5. // this will fire the request
  6. .subscribeNext { json in
  7. print(json)
  8. }
  9. NSThread.sleepForTimeInterval(3)
  10. // if you want to cancel request after 3 seconds have passed just call
  11. cancelRequest.dispose()

NSURLSession extensions don’t return result on MainScheduler by default.

In case you want a more low level access to response, you can use:

  1. NSURLSession.sharedSession().rx_response(myNSURLRequest)
  2. .debug("my request") // this will print out information to console
  3. .flatMap { (data: NSData!, response: NSURLResponse!) -> Observable<String> in
  4. if let response = response as? NSHTTPURLResponse {
  5. if 200 ..< 300 ~= response.statusCode {
  6. return just(transform(data))
  7. }
  8. else {
  9. return Observable.error(yourNSError)
  10. }
  11. }
  12. else {
  13. rxFatalError("response = nil")
  14. return Observable.error(yourNSError)
  15. }
  16. }
  17. .subscribe { event in
  18. print(event) // if error happened, this will also print out error to console
  19. }

Logging HTTP traffic

In debug mode RxCocoa will log all HTTP request to console by default. In case you want to change that behavior, please set Logging.URLRequests filter.

  1. // read your own configuration
  2. public struct Logging {
  3. public typealias LogURLRequest = (NSURLRequest) -> Bool
  4. public static var URLRequests: LogURLRequest = { _ in
  5. #if DEBUG
  6. return true
  7. #else
  8. return false
  9. #endif
  10. }
  11. }

RxDataSources

… is a set of classes that implement fully functional reactive data sources for UITableViews and UICollectionViews.

RxDataSources are bundled here.

Fully functional demonstration how to use them is included in the RxExample project.