Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reactive Extensions for Mobile

Reactive Extensions for Mobile

Talk Video: https://atscaleconference.com/videos/mobilescale-tel-aviv-reactive-extensions-for-mobile-apps-getting-started/

Reactive Applications have been extremely popular in all fields of programming: web apps, backend, and of course mobile applications! In this talk, Shai Mishali, iOS Tech Lead with Gett, discusses the building blocks of Reactive Extensions and Reactive Programming, and how you can leverage these concepts and techniques in your own applications, across platforms.

from Facebook's Mobile@Scale in Tel Aviv, Israel

Shai Mishali

December 10, 2018
Tweet

More Decks by Shai Mishali

Other Decks in Technology

Transcript

  1. About Me Shai Mishali @freak4pc iOS Tech Lead @ Gett

    Open Source = ❤ Won some hackathons Battlehack Tel Aviv 2014
 Battlehack World Finals 2014 Ford Dev Challenge 2015 iOS Team @ RW
  2. Agenda • What is Rx & Why you’d want to

    use it? • Rx Building Blocks • Defining Behaviors with Streams • Differences between RxSwift and RxJava • Debunking some Myths • Where to go next? And of course … An absurd amount of Memes
  3. Observers / Subscribers the observer pattern “A software design pattern

    in which an object (Observable) maintains a list of Observers and notifies them automatically of changes.” isReady
 Observable View 1
 Observer Some
 Manager
 Observer Activity
 Indicator Observer View 2 Observer
  4. What is Rx? - Focusing on Data Flow and Defined

    Behavior to
 drive your application. - A new concept to most iOS developers, more popular in the Android community. - High learning curve - This lecture won’t make you an
 Rx expert. - Most likely will bend your brain a bit until it “clicks”. Be Patient !
  5. WHY USE RX? • Provides a unified API for dealing

    with asynchronous actions (e.g. Observable). • Provides a mechanism to Reactively handle and manipulate Values over Time. ⏳ • Focus on Writing Declarative and Expected Code that defines behaviour. Data is king. • The rise of Kotlin makes inter-team logic sharing/ discussion easier than ever. • Encourages good architecture.
  6. multi-platform Moving between platforms is relatively seamless, since ReactiveX provides

    a standard implemented in many languages.
 
 Learn once, use anywhere RxSwift RxJava RxKotlin Rx.NET RxJS RxDart RxGo and many more …
  7. Companies using Rx Rx is mature and has been in

    use by some of the biggest tech companies
  8. REACTIVE let canMakePurchase = Observable .combineLatest(userBalance, productPrice) .map { $0

    >= $1 } // Observable<Bool> canMakePurchase ALWAYS reflects the latest state canMakePurchase doesn’t reflect the latest state var userBalance = 5 let productPrice = 10 let canMakePurchase = userBalance >= productPrice print(canMakePurchase) userBalance += 20 print(canMakePurchase) // false // STILL false!
  9. Observable Int Independent Values 1 52 1337 52 70000 -24

    404 Observable<Int> Values over time 40 1 404 -24 1337
  10. observable lifecycle An Observable (aka Stream) may emit any number

    of next(value) events next next next next next next next next error X next next next completed X next When it emits a single error or completed event, the sequence will terminate
  11. everything is a stream Any values/events over time can be

    modeled as an Observable Stream tap Taps on a button: tap tap tap tap tap tap tap Networking: Array of Results completed X Number of products in a shopping cart: 1 2 5 4 5 0
  12. subscribing let numbers = Observable.of(1, 4, -24, 400) numbers.subscribe( onNext:

    { value in print("Got next: \(value)") }, onError: { error in print("An error occurred: \(error)") }, onCompleted: { print("Completed") }) .disposed(by: disposeBag) val numbers = Observable .from(listOf(1, 4, -24, 400)) numbers.subscribeBy( onNext = { println("Got next: $it") }, onError = { println("Got error: $it") }, onComplete = { println("Completed") } )
  13. subscribing let numbers = Observable.of(1, 4, -24, 400) numbers.subscribe( onNext:

    { value in print("Got next: \(value)") }, onError: { error in print("An error occurred: \(error)") }, onCompleted: { print("Completed") }) .disposed(by: disposeBag) val numbers = Observable .from(listOf(1, 4, -24, 400)) numbers.subscribeBy( onNext = { println("Got next: $it") }, onError = { println("Got error: $it") }, onComplete = { println("Completed") } )
  14. subscribing let numbers = Observable.of(1, 4, -24, 400) numbers.subscribe( onNext:

    { value in print("Got next: \(value)") }, onError: { error in print("An error occurred: \(error)") }, onCompleted: { print("Completed") }) .disposed(by: disposeBag) val numbers = Observable .from(listOf(1, 4, -24, 400)) numbers.subscribeBy( onNext = { println("Got next: $it") }, onError = { println("Got error: $it") }, onComplete = { println("Completed") } )
  15. subscribing let numbers = Observable.of(1, 4, -24, 400) numbers.subscribe( onNext:

    { value in print("Got next: \(value)") }, onError: { error in print("An error occurred: \(error)") }, onCompleted: { print("Completed") }) .disposed(by: disposeBag) val numbers = Observable .from(listOf(1, 4, -24, 400)) numbers.subscribeBy( onNext = { println("Got next: $it") }, onError = { println("Got error: $it") }, onComplete = { println("Completed") } )
  16. subscribing let numbers = Observable.of(1, 4, -24, 400) numbers.subscribe( onNext:

    { value in print("Got next: \(value)") }, onError: { error in print("An error occurred: \(error)") }, onCompleted: { print("Completed") }) .disposed(by: disposeBag) val numbers = Observable .from(listOf(1, 4, -24, 400)) numbers.subscribeBy( onNext = { println("Got next: $it") }, onError = { println("Got error: $it") }, onComplete = { println("Completed") } )
  17. subscribing let numbers = Observable.of(1, 4, -24, 400) numbers.subscribe( onNext:

    { value in print("Got next: \(value)") }, onError: { error in print("An error occurred: \(error)") }, onCompleted: { print("Completed") }) .disposed(by: disposeBag) val numbers = Observable .from(listOf(1, 4, -24, 400)) numbers.subscribeBy( onNext = { println("Got next: $it") }, onError = { println("Got error: $it") }, onComplete = { println("Completed") } )
  18. let numbers = Observable.of(1, 4, -24, 400) numbers.subscribe( onNext: {

    value in print("Got next: \(value)") }, onError: { error in print("An error occurred: \(error)") }, onCompleted: { print("Completed") }) .disposed(by: disposeBag) val numbers = Observable .from(listOf(1, 4, -24, 400)) numbers.subscribeBy( onNext = { println("Got next: $it") }, onError = { println("Got error: $it") }, onComplete = { println("Completed") } ) subscribing Got next: 1 Got next: 4 Got next: -24 Got next: 400 Console output: Completed
  19. Declaring Behaviors One of the most powerful aspects of Rx

    is composing, manipulating and transforming Observables into other Observables in a way that defines Behavior (e.g. Declarative) Button tap Network Request Transformed to … Transformed to… Array of Models Driving … UITableView
 RecyclerView Array All People Array Favorite people Filtered into … Driving … UITableView
 RecyclerView username String password String isValid Bool Button isEnabled Button Title Composed into … Button Title Driving … Driving … Transformed to
  20. Declaring Behaviors One of the most powerful aspects of Rx

    is composing, manipulating and transforming Observables into other Observables in a way that defines Behavior (e.g. Declarative) Button tap Network Request Transformed to … Transformed to… Array of Models Driving … UITableView RecyclerView Array All People Array Favorite people Filtered into … Driving … UITableView RecyclerView username String password String isValid Bool Button isEnabled Button Title Composed into … Button Title Driving … Driving … Transformed to
  21. Operators • Lets you manipulate the Observable stream. • Most

    operators act the same between platforms, but some languages have behaviour specific to them. • Most of them are Chainable (operator works on an Observable and returns an Observable). • Many “extra” operators exist in the community. • You can quite easily make your own.
  22. Defining Behaviors // Observable<Store> store = latestLocation .flatMapLatest { storeService.getNearby(coordinate:

    $0) .retry(3) } // Observable<String> title = store.map { “Ordering from {$0.name}" }
  23. Defining Behaviors // Observable<Store> store = latestLocation .throttle(0.3) .flatMapLatest {

    storeService.getNearby(coordinate: $0) .retry(3) } // Observable<String> title = store.map { “Ordering from {$0.name}" }
  24. Defining Behaviors // Observable<[MenuItem]> menu = store .flatMapLatest { menuService.getMenu(for:

    $0.id) .retry(3) } // Observable<String> subtitle = menu.map { "Store has {$0.count} items" }
  25. Defining Behaviors // store - Observable<Store> // menu - Observable<[MenuItem]>

    // Observable<Bool> canMakeOrder = Observable .combineLatest(store, menu) .map { $0.isOpen && !$1.isEmpty } // Observable<String> buttonTitle = canMakeOrder .map { $0 ? "Add some items!” : “Store unavailable" }
  26. Defining Behaviors // store - Observable<Store> // Observable<([MenuItem], Distance)> menuAndDistance

    = store .flatMapLatest { Observable.zip( menuService.getMenu(for: $0.id), etaService.getETA(to: $0.location) ) } // Observable<String> subtitle = menuAndDistance .map { "Store has {$0.menu.count} “ + “items and is {$0.distance)m away" }
  27. Defining Behaviors // Observable<Store> store = Observable.merge( locationChanged, tappedRefresh )

    .withLatestFrom(latestLocation) .flatMapLatest { storeService.getNearby(coordinate: $0) .retry(3)
 }
  28. Defining Behaviors // Observable<Store> store = Observable.merge( locationChanged, tappedRefresh, becameReachable

    ) .withLatestFrom(latestLocation) .flatMapLatest { storeService.getNearby(coordinate: $0) .retry(3)
 }
  29. Defining Behaviors currentLocation store subtitle canMakeOrder buttonTitle menu tappedRefresh becameReacahable

    menuAndDistance Button Title & Enabled state title Navigation Title & Subtitle
  30. Defining Behaviors currentLocation store subtitle canMakeOrder buttonTitle menu tappedRefresh becameReacahable

    menuAndDistance Button Title & Enabled state title Navigation Title & Subtitle
  31. Error Handling Operators Transform Filter Create Combine Utilities A full

    list of operators is available at http://reactivex.io/documentation/operators.html create just of from deferred map flatMap flatMapLatest scan filter throttle debounce distinctUntilChanged skip take first last observeOn subscribeOn delay do materialize dematerialize retry retryWhen catchError catchErrorJustReturn combineLatest merge concat startWith zip
  32. Rx + Mobile UI ??? RxSwift RxJava Neutral implementation Based

    on ReactiveX iOS & macOS, tvOS, watchOS Android Now that we have our streams, how do we actually “feed” values from them into our App’s UI Elements, and the other way around ?
  33. Rx + Mobile UI Streams of UI components RxCocoa (part

    of RxSwift) lets you “subscribe” to events/ properties on most standard UI components as Observable streams under the rx namespace, as well as most delegates. Easily customizable. RxBinding (by Jake Wharton, not part of RxJava) lets you “subscribe” to a similar set of most basic UI components. Custom components aren’t entirely trivial. button.rx.tap textField.rx.text slider.rx.value collection.rx.itemSelected datePicker.rx.date RxView.clicks(button) RxView.focusChanges(editText) RxTextView.textChanges(editText) RxSeekBar.userChanges
  34. Rx + Mobile UI Feeding streams into UI components RxCocoa

    provides a special unit called a Binder that lets you bind streams directly into UI components. You can easily create your own bindings for custom components. Bindings aren’t as popular or trivial and are used mainly by more “hardcore” Rx people. Standard use cases simply use the standard subscribe for the stream. textStream.bind(to: lblTitle.rx.text) isFormValid.bind(to: btnSubmit.rx.isEnabled) buttonTitle.bind(to: button.rx.title()) yourStream.bind(to: customView.rx.yourProperty) textStream.subscribe( { textView.text = $it }, { Log.e("MainActivity", "$it") } )
  35. It’s insanely hard! RxSwift: Use .debug() operator RxJava: Log in

    doOnEach() or use a breakpoint Learning Curve is High, but once you “Think Rx”, it’s hard to go back :-) RxSwift: Use a DisposeBag
 RxJava: JVM Garbage Collection / CompositeDisposable Ob ser ver Ob ser ver Obser Obser ver 2 DisposeBag
  36. Want to learn more? Official RxSwift & RxJava Documentation: http://github.com/ReactiveX/RxSwift

    https://github.com/ReactiveX/RxJava/tree/2.x/docs ReactiveX Documentation: http://reactivex.io/documentation/observable.html RxMarbles: http://rxmarbles.com/ RxSwift: Reactive Programming with Swift (RayWenderlich): Reactive Programming with RxJava (O’Reilly): https://store.raywenderlich.com/products/rxswift-reactive-programming-with-swift https://www.oreilly.com/library/view/reactive-programming-with/9781491931646/