Examples

  1. Calculated variable
  2. Simple UI bindings
  3. Autocomplete
  4. more examples
  5. Playgrounds

Calculated variable

First, let’s start with some imperative code. The purpose of this example is to bind the identifier c to a value calculated from a and b if some condition is satisfied.

Here is the imperative code that calculates the value of c:

  1. // this is standard imperative code
  2. var c: String
  3. var a = 1 // this will only assign the value `1` to `a` once
  4. var b = 2 // this will only assign the value `2` to `b` once
  5. if a + b >= 0 {
  6. c = "\(a + b) is positive" // this will only assign the value to `c` once
  7. }

The value of c is now 3 is positive. However, if we change the value of a to 4, c will still contain the old value.

  1. a = 4 // `c` will still be equal to "3 is positive" which is not good
  2. // we want `c` to be equal to "6 is positive" since 4 + 2 = 6

This is not the desired behavior.

This is the improved logic using RxSwift:

  1. let a /*: Observable<Int>*/ = Variable(1) // a = 1
  2. let b /*: Observable<Int>*/ = Variable(2) // b = 2
  3. // combines latest values of variables `a` and `b` using `+`
  4. let c = Observable.combineLatest(a.asObservable(), b.asObservable()) { $0 + $1 }
  5. .filter { $0 >= 0 } // if `a + b >= 0` is true, `a + b` is passed to the map operator
  6. .map { "\($0) is positive" } // maps `a + b` to "\(a + b) is positive"
  7. // Since the initial values are a = 1 and b = 2
  8. // 1 + 2 = 3 which is >= 0, so `c` is initially equal to "3 is positive"
  9. // To pull values out of the Rx `Observable` `c`, subscribe to values from `c`.
  10. // `subscribeNext` means subscribe to the next (fresh) values of `c`.
  11. // That also includes the initial value "3 is positive".
  12. c.subscribeNext { print($0) } // prints: "3 is positive"
  13. // Now, let's increase the value of `a`
  14. a.value = 4 // prints: 6 is positive
  15. // The sum of the latest values, `4` and `2`, is now `6`.
  16. // Since this is `>= 0`, the `map` operator produces "6 is positive"
  17. // and that result is "assigned" to `c`.
  18. // Since the value of `c` changed, `{ print($0) }` will get called,
  19. // and "6 is positive" will be printed.
  20. // Now, let's change the value of `b`
  21. b.value = -8 // doesn't print anything
  22. // The sum of the latest values, `4 + (-8)`, is `-4`.
  23. // Since this is not `>= 0`, `map` doesn't get executed.
  24. // This means that `c` still contains "6 is positive"
  25. // Since `c` hasn't been updated, a new "next" value hasn't been produced,
  26. // and `{ print($0) }` won't be called.

Simple UI bindings

  • Instead of binding to variables, let’s bind to UITextField values using the rx_text property
  • Next, map the String into an Int and determine if the number is prime using an async API
  • If the text is changed before the async call completes, a new async call will replace it via concat
  • Bind the results to a UILabel
  1. let subscription/*: Disposable */ = primeTextField.rx_text // type is Observable<String>
  2. .map { WolframAlphaIsPrime(Int($0) ?? 0) } // type is Observable<Observable<Prime>>
  3. .concat() // type is Observable<Prime>
  4. .map { "number \($0.n) is prime? \($0.isPrime)" } // type is Observable<String>
  5. .bindTo(resultLabel.rx_text) // return Disposable that can be used to unbind everything
  6. // This will set `resultLabel.text` to "number 43 is prime? true" after
  7. // server call completes.
  8. primeTextField.text = "43"
  9. // ...
  10. // to unbind everything, just call
  11. subscription.dispose()

All of the operators used in this example are the same operators used in the first example with variables. There’s nothing special about it.

Autocomplete

If you are new to Rx, the next example will probably be a little overwhelming at first. However, it’s here to demonstrate how RxSwift code looks in the real-world.

This example contains complex async UI validation logic with progress notifications. All operations are cancelled the moment disposeBag is deallocated.

Let’s give it a shot.

  1. // bind UI control values directly
  2. // use username from `usernameOutlet` as username values source
  3. self.usernameOutlet.rx_text
  4. .map { username in
  5. // synchronous validation, nothing special here
  6. if username.isEmpty {
  7. // Convenience for constructing synchronous result.
  8. // In case there is mixed synchronous and asynchronous code inside the same
  9. // method, this will construct an async result that is resolved immediately.
  10. return Observable.just((valid: false, message: "Username can't be empty."))
  11. }
  12. // ...
  13. // User interfaces should probably show some state while async operations
  14. // are executing.
  15. // Let's assume that we want to show "Checking availability" while waiting for a result.
  16. // Valid parameters can be:
  17. // * true - is valid
  18. // * false - is not valid
  19. // * nil - validation pending
  20. typealias LoadingInfo = (valid: String?, message: String?)
  21. let loadingValue : LoadingInfo = (valid: nil, message: "Checking availability ...")
  22. // This will fire a server call to check if the username already exists.
  23. // Its type is `Observable<ValidationResult>`
  24. return API.usernameAvailable(username)
  25. .map { available in
  26. if available {
  27. return (true, "Username available")
  28. }
  29. else {
  30. return (false, "Username already taken")
  31. }
  32. }
  33. // use `loadingValue` until server responds
  34. .startWith(loadingValue)
  35. }
  36. // Since we now have `Observable<Observable<ValidationResult>>`
  37. // we need to somehow return to a simple `Observable<ValidationResult>`.
  38. // We could use the `concat` operator from the second example, but we really
  39. // want to cancel pending asynchronous operations if a new username is provided.
  40. // That's what `switchLatest` does.
  41. .switchLatest()
  42. // Now we need to bind that to the user interface somehow.
  43. // Good old `subscribeNext` can do that.
  44. // That's the end of `Observable` chain.
  45. .subscribeNext { valid in
  46. errorLabel.textColor = validationColor(valid)
  47. errorLabel.text = valid.message
  48. }
  49. // This will produce a `Disposable` object that can unbind everything and cancel
  50. // pending async operations.
  51. // Instead of doing it manually, which is tedious,
  52. // let's dispose everything automagically upon view controller dealloc.
  53. .addDisposableTo(disposeBag)

It doesn’t get any simpler than that. There are more examples in the repository, so feel free to check them out.

They include examples on how to use Rx in the context of MVVM pattern or without it.