Slide 1

Slide 1 text

Debunking the Myth of Hard + = RxSwift

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Agenda • What is Rx & Why you’d want to use it • RxSwift’s Building Blocks • RxCocoa: RxSwift’s UI companion • Debunking some Myths • Where to go next? And of course … An absurd amount of Memes

Slide 4

Slide 4 text

What is rx?

Slide 5

Slide 5 text

What is Rx? “An API for asynchronous programming with observable streams” - reactivex.io

Slide 6

Slide 6 text

What is Rx?

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

What is Rx? - Focusing on Data Flow and Defined Behavior to
 drive your application - A new concept to most developers - 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 !

Slide 9

Slide 9 text

Why use rx?

Slide 10

Slide 10 text

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. • Lets you focus on writing Declarative and Expected Code that defines behavior instead of imperative code. Meaning, lets values and data drive the app instead of “manually” refreshing / resetting state. • Encourages good architecture.

Slide 11

Slide 11 text

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 …

Slide 12

Slide 12 text

Companies using Rx Rx is mature and has been in use by some of the biggest tech companies

Slide 13

Slide 13 text

REACTIVE let canMakePurchase = Observable .combineLatest(userBalance, productPrice) .map { $0 >= $1 } // Observable 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!

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Simple Easy

Slide 16

Slide 16 text

Rx Building Blocks

Slide 17

Slide 17 text

Observable Int Independent Values 1 52 1337 52 70000 -24 404 Observable Values over time 40 1 404 -24 1337

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

everything is a stream

Slide 20

Slide 20 text

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: [Result] completed X Number of products in a shopping cart: 1 2 5 4 5 0

Slide 21

Slide 21 text

Observable Notification Center
 KVO
 Delegates Bindings
 ↔ UI
 Events
 Networking
 everything is a stream

Slide 22

Slide 22 text

subscribing The act of listening or observing events emitted by
 an Observable is called Subscribing.

Slide 23

Slide 23 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 24

Slide 24 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 25

Slide 25 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 26

Slide 26 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 27

Slide 27 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 28

Slide 28 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 29

Slide 29 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 30

Slide 30 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) Got next: 1 Got next: 4 Got next: -24 Got next: 400 subscribing Console output: Completed Disposed

Slide 31

Slide 31 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) subscribing

Slide 32

Slide 32 text

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") }, onDisposed: { print("Disposed") }) .disposed(by: disposeBag) DisposeBag class ViewController: UIViewController { let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() ... ... } }

Slide 33

Slide 33 text

DisposeBag Obs erver 4 Obs erver 3 Observ er 1 Observ er 2 observable .subscribe(onNext: { item in // handle item }) .disposed(by: disposeBag) DisposeBag ViewController / Owner ViewController / Owner Deinit DisposeBag Deinit Observables in Bag Terminate & Dispose Created Subscription (In-Memory)

Slide 34

Slide 34 text

Subject Observables are read only, but sometimes you need a type that you can write events into, or bind events into. That type is called an Observer. RxSwift provides an abstraction called Subject that is both an Observer and an Observable. Meaning, you can write events into the stream, and also subscribe to read events from it) Rx provides 4 types of Subjects: PublishSubject ReplaySubject BehaviorSubject AsyncSubject

Slide 35

Slide 35 text

Subject PublishSubject ReplaySubject BehaviorSubject Doesn’t replay elements to new subscribers Replays latest element to new subscribers Replays any number of element to new subscribers A B C D Subscription starts here D PublishSubject() D C BehaviorSubject(value: "A") B C D ReplaySubject.create(bufferSize: 2) let subject = ??? subject.onNext("A") subject.onNext("B") subject.onNext("C") subject.subscribe( ...) subject.onNext("D")

Slide 36

Slide 36 text

Subject vs. Observable Subject Write Read Observable Read next next error next completed Events For the sake of simplicity you can think of an Observable as sort of an Output, while Subjects act more like Inputs.

Slide 37

Slide 37 text

subscribing

Slide 38

Slide 38 text

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… [Model] Driving … UITableView [Person] All People [Person] Favorite people Filtered into … Driving … UITableView username String password String isValid Bool Button isEnabled Button Title Composed into … Button Title Driving … Driving … Transformed to

Slide 39

Slide 39 text

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… [Model] Driving … UITableView [Person] All People [Peron] Favorite people Filtered into … Driving … UITableView username String password String isValid Bool Button isEnabled Button Title Composed into … Button Title Driving … Driving … Transformed to

Slide 40

Slide 40 text

Operators

Slide 41

Slide 41 text

Operators • Lets you manipulate the Observable stream • Most operators act the same between platforms, but some languages have behavior specific to them • Adds a "Mathematical" aspect to Rx • 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

Slide 42

Slide 42 text

Operators Transform Filter Create Combine Error Handling Utilities

Slide 43

Slide 43 text

Operators ⌨ Throttle text input + Make sure we avoid duplicates Get JSON from Server ↩ Retry the request if it fails ⚔ If there are previous requests, cancel them Map to a Person Model Make sure we clear up memory when
 leaving the screen Our acceptance criteria: Make sure we can actually understand this
 code later

Slide 44

Slide 44 text

Operators ⌨ Throttle text input + Make sure we avoid duplicates Get JSON from Server ↩ Retry the request if it fails ⚔ If there are previous requests, cancel them Map to a Person Model Make sure we clear up memory when
 leaving the screen Our acceptance criteria: Make sure we can actually understand this
 code later UITableView Delegate UITableView Data Source UISearchController Delegate UITextField Delegate URLSession Handler Custom throttling logic Custom duplicate logic HTTP request cancellation mechanism Custom retry logic

Slide 45

Slide 45 text

Operators ⌨ Throttle text input + Make sure we avoid duplicates Get JSON from Server ↩ Retry the request if it fails ⚔ If there are previous requests, cancel them Map to a Person Model Make sure we clear up memory when
 leaving the screen Our acceptance criteria: Make sure we can actually understand this
 code later UITableView Delegate UITableView Data Source UISearchController Delegate UITextField Delegate URLSession Handler Custom throttling logic Custom duplicate logic HTTP request cancellation mechanism Custom retry logic Painful, hard to maintain piece of code

Slide 46

Slide 46 text

Operators

Slide 47

Slide 47 text

Operators So, let’s say we do it in … 10 Lines of Code. We want to make things more readable and easier to understand! Shit just got real.

Slide 48

Slide 48 text

Operators searchBar.rx.text.orEmpty .throttle(0.3, scheduler: MainScheduler.instance) .distinctUntilChanged() // Observable .flatMapLatest { API.getResults($0) .retry(3) } // Observable<[[String: Any]]> .map { Person.fromJSONArray($0) } // Observable<[Person]> .observeOn(MainScheduler.instance) .bind(to: tableView.rx.items(cellIdentifier: “Cell")) { row, person, cell in cell.textLabel ?.text = "\(person.name) @ row \(row)" } .disposed(by: disposeBag)

Slide 49

Slide 49 text

Operators searchBar.rx.text.orEmpty .throttle(0.3, scheduler: MainScheduler.instance) .distinctUntilChanged() // Observable .flatMapLatest { API.getResults($0) .retry(3) } // Observable<[[String: Any]]> .map { Person.fromJSONArray($0) } // Observable<[Person]> .observeOn(MainScheduler.instance) .bind(to: tableView.rx.items(cellIdentifier: “Cell")) { row, person, cell in cell.textLabel ?.text = "\(person.name) @ row \(row)" } .disposed(by: disposeBag)

Slide 50

Slide 50 text

Operators searchBar.rx.text.orEmpty .throttle(0.3, scheduler: MainScheduler.instance) .distinctUntilChanged() // Observable .flatMapLatest { API.getResults($0) .retry(3) } // Observable<[[String: Any]]> .map { Person.fromJSONArray($0) } // Observable<[Person]> .observeOn(MainScheduler.instance) .bind(to: tableView.rx.items(cellIdentifier: “Cell")) { row, person, cell in cell.textLabel ?.text = "\(person.name) @ row \(row)" } .disposed(by: disposeBag) These are all Operators !

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Operators Ok - so I know about Observables and how to use some Operators, but …

Slide 53

Slide 53 text

RxCocoa

Slide 54

Slide 54 text

??? RxCocoa RxSwift Neutral implementation Based on ReactiveX iOS (UIKit) macOS (AppKit) watchOS (WatchKit) tvOS (UIKit)

Slide 55

Slide 55 text

??? RxCocoa Platform-specific Rx additions RxCocoa The glue that connects Observable streams to iOS, macOS, watchOS and tvOS UI Components RxSwift Neutral implementation Based on ReactiveX iOS (UIKit) macOS (AppKit) watchOS (WatchKit) tvOS (UIKit)

Slide 56

Slide 56 text

RxCocoa & UI RxCocoa provides two Traits that represent properties and events on UI elements, and an Observer named Binder that lets you bind streams to UI elements ControlEvent ControlProperty *** Describes properties of UI Elements, e.g. UITextField -> text UISlider -> value
 UISwitch -> isOn etc… Describes events on UI Elements, e.g. tap changed didScroll etc… Describes bindings to UI Elements, e.g. alpha isEnabled isHidden etc… Binder

Slide 57

Slide 57 text

RxSwift & UI ControlEvent ControlProperty *** RxCocoa covers most of the common UI elements in UIKit, AppKit, etc. Simply add .rx after your UI element to start discovering Binder button.rx.isEnabled button.rx.title(for: .normal) view.rx.isHidden view.rx.alpha textField.rx.text slider.rx.value datePicker.rx.date scrollView.rx.contentOffset switch.rx.isOn button.rx.tap collection.rx.itemSelected textView.rx.didBeginEditing gestureRec.rx.event

Slide 58

Slide 58 text

Connecting the pieces Text field is empty Button is disabled Text field filled Button is enabled Button pressed - label changes based on Text Field

Slide 59

Slide 59 text

DEFINING BEHAVIORS Text field is empty Button is disabled Text field filled Button is enabled Button pressed - label changes based on Text Field

Slide 60

Slide 60 text

DEFINING BEHAVIORS txtName.rx.text.orEmpty // Observable .map { !$0.isEmpty } // Observable btnSend.rx.tap .withLatestFrom(txtName.rx.text.orEmpty) .map { "Hey there, \($0)!” } // Observable .subscribe(onNext: { [unowned self] isEnabled in self.btnSend.isEnabled = isEnabled }) .disposed(by: disposeBag) .subscribe(onNext: { [unowned self] text in self.lblText.text = text }) .disposed(by: disposeBag)

Slide 61

Slide 61 text

btnSend.rx.tap .withLatestFrom(txtName.rx.text.orEmpty) .map { "Hey there, \($0)!” } // Observable DEFINING BEHAVIORS txtName.rx.text.orEmpty // Observable .map { !$0.isEmpty } // Observable .bind(to: btnSend.rx.isEnabled) .disposed(by: disposeBag) .bind(to: lblText.rx.text) .disposed(by: disposeBag)

Slide 62

Slide 62 text

DEFINING BEHAVIORS txtName.rx.text.orEmpty .map { $0.isEmpty ? "" : "Hey there, \($0)!" } .bind(to: lblText.rx.text) .disposed(by: disposeBag)

Slide 63

Slide 63 text

DEFINING BEHAVIORS txtName.rx.text.orEmpty .bind(to: lblText.rx.text) .disposed(by: disposeBag)

Slide 64

Slide 64 text

Extra Cocoa! ☕ RxCocoa provides many more abilities out of the scope of this lecture, for example: Driver Custom Binder Relay Signal

Slide 65

Slide 65 text

RxCocoa

Slide 66

Slide 66 text

Debunking some Myths

Slide 67

Slide 67 text

There are 2 main myths ✌

Slide 68

Slide 68 text

You have to go
 “All or Nothing”

Slide 69

Slide 69 text

It’s insanely hard! Use the .debug() operator to make your life easier Learning Curve is High, but once you “Think Rx”, it’s hard to go back :-) Use a DisposeBag and make
 sure everything is correctly
 disposed Ob ser ver Ob ser ver Obser Obser ver 2 DisposeBag

Slide 70

Slide 70 text

Next Steps

Slide 71

Slide 71 text

Awesome Job! You made it !

Slide 72

Slide 72 text

3rd party Libraries RxSwift’s Community is thriving with amazing Open-Source projects, be it wrappers around libraries you know, or entirely new Libraries. To find an Rx wrapper for a library or component you use, simply add Rx before, and you’ll probably find it available online ;-) RxSwiftCommunity @ GitHub RxAlamofire RxRealm RxGoogleMaps RxMoya RxStarscream RxMKMapView Other recommended libraries: RxSwiftExt RxDataSources Action RxAnimated RxFeedback ReactorKit And many many more...

Slide 73

Slide 73 text

Got stuck? Have QUESTIONS? The best place to ask questions is in our awesome & welcoming Slack community. Feel free to join us ! + = http://slack.rxswift.org

Slide 74

Slide 74 text

Want to learn more? Official RxSwift Documentation: http://github.com/ReactiveX/RxSwift Adam Borek’s blog: http://adamborek.com Marin Todorov’s Blog: http://rx-marin.com RxSwift: Reactive Programming with Swift (Book) https://store.raywenderlich.com/products/rxswift ReactiveX Documentation: http://reactivex.io/documentation/observable.html RxMarbles iOS App https://appstore.com/rxmarbles

Slide 75

Slide 75 text

Thank you! Shai Mishali freak4pc Questions?