Slide 1

Slide 1 text

REACTIVE THINKING IN IOS DEVELOPMENT @PEPIBUMUR / @SAKY

Slide 2

Slide 2 text

WHO? @pepibumur iOS Developer at SoundCloud GitHub: pepibumur Twitter: pepibumur @saky iOS Developer at Letgo GitHub: isaacroldan Twitter: saky GitDo.io our spare time project

Slide 3

Slide 3 text

INDEX > Programming Paradigms > Reactive Libraries > Reactive Motivation > Reactive Thinking > Reactive Caveats > Conclusion

Slide 4

Slide 4 text

PARADIGMS ! WAYS OF SEEING THE WORLD WHEN IT COMES TO PROGRAMMING

Slide 5

Slide 5 text

WIKIPEDIA Data-Driven, Declarative, Dynamic, End-User, Event-Driven, Expression-Oriented, Feature-Oriented, Function-level, Generic, Imperative, Inductive, Language Oriented, Metaprogramming, Non-Structured, Nondeterministic, Parallel computing, Point-free Style, Structured, Value- Level, Probabilistic

Slide 6

Slide 6 text

IMPERATIVE PROGRAMMING DECLARATIVE PROGRAMMING

Slide 7

Slide 7 text

IMPERATIVE PROGRAMMING DECLARATIVE PROGRAMMING

Slide 8

Slide 8 text

HOW SEQUENCE OF STEPS THAT HAPPEN IN ORDER

Slide 9

Slide 9 text

NATURAL WAY TO PROGRAM

Slide 10

Slide 10 text

EXECUTION STATE (AKA SIDE EFFECT)

Slide 11

Slide 11 text

IMPERATIVE PROGRAMMING func userDidSearch(term: String) { let apiReults = api.search(term: term).execute() self.items = self.adaptResults(apiResults) self.tableView.reloadData() }

Slide 12

Slide 12 text

IMPERATIVE PROGRAMMING DECLARATIVE PROGRAMMING

Slide 13

Slide 13 text

WHAT IT DOESN'T DESCRIBE THE CONTROL FLOW

Slide 14

Slide 14 text

HTML

Slide 15

Slide 15 text

HTML SQL

Slide 16

Slide 16 text

HTML SQL REACTIVE PROGRAMMING

Slide 17

Slide 17 text

DECLARATIVE PROGRAMMING let predicate = NSPredicate(format: "name == %@", "Pedro") let regex = NSRegularExpression(pattern: ".+", options: 0)

Slide 18

Slide 18 text

REACTIVE PROGRAMMING DATA-FLOW PROGRAMMING (DESCRIBES THE STATE PROPAGATION) # THE INTRODUCTION TO REACTIVE PROGRAMMING THAT YOU'VE BEEN MISSING

Slide 19

Slide 19 text

FUNCTIONAL REACTIVE PROGRAMMING (AKA FRP)

Slide 20

Slide 20 text

REACTIVE LIBRARIES > RxSwift (ReactiveX) > ReactiveCocoa > BrightFutures > ReactKit > Bond > More and more... PromiseKit, Bolts...

Slide 21

Slide 21 text

WHAT LIBRARY SHOULD I USE? !

Slide 22

Slide 22 text

WHAT LIBRARY SHOULD I USE? ! DO I NEED A LIBRARY FOR THIS? !

Slide 23

Slide 23 text

SWIFT var userName: String { didSet { // React to changes in variables } }

Slide 24

Slide 24 text

SWIFT var userName: String { didSet { // React to changes in variables view.updateTitle(userName) } }

Slide 25

Slide 25 text

MOTIVATION ! WHY SHOULD I STICK TO REACTIVE PROGRAMMING?

Slide 26

Slide 26 text

> Bindings > Composability > Threading

Slide 27

Slide 27 text

> Bindings > Composability > Threading

Slide 28

Slide 28 text

BINDING

Slide 29

Slide 29 text

UI BINDING

Slide 30

Slide 30 text

UI BINDING

Slide 31

Slide 31 text

UI BINDING

Slide 32

Slide 32 text

> Bindings > Composability > Threading

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

> Bindings > Composability > Threading

Slide 36

Slide 36 text

> Bindings > Composability > Threading (observer and execution)

Slide 37

Slide 37 text

THREADING

Slide 38

Slide 38 text

REACTIVE THINKING !

Slide 39

Slide 39 text

THINKING IN TERMS OF OBSERVABLES OR SIGNALS/PRODUCERS IN REACTIVECOCOA

Slide 40

Slide 40 text

ACTIONS CAN BE OBSERVED HOW? !

Slide 41

Slide 41 text

.NEXT(T) .ERROR(ERRORTYPE) .COMPLETE

Slide 42

Slide 42 text

OPERATIONS RxSwift Observable.create { (observer) -> Disposable in observer.onNext("next value") observer.onCompleted() return NopDisposable.instance // For disposing the action } ReactiveCocoa SignalProducer.create { (observer, disposable) in observer.sendNext("next value") observer.sendComplete() }

Slide 43

Slide 43 text

EXISTING PATTERNS RXSWIFT ▶︎ RXCOCOA (EXTENSIONS) REACTIVECOCOA ▶︎ DO IT YOURSELF

Slide 44

Slide 44 text

UIKIT let button = UIButton() button.rx_controlEvent(.TouchUpInside) .subscribeNext { _ in print("The button was tapped") }

Slide 45

Slide 45 text

NOTIFICATIONS NSNotificationCenter.defaultCenter() .rx_notification("my_notification", object: nil) .subscribeNext { notification // We got a notification }

Slide 46

Slide 46 text

DELEGATES self.tableView.rx_delegate // DelegateProxy .observe(#selector(UITableViewDelegate.tableView(_:didSelectRowAtIndexPath:))) .subscribeNext { (parameters) in // User did select cell at index path }

Slide 47

Slide 47 text

PLAYING ! WITH OBSERVABLES

Slide 48

Slide 48 text

OBSERVABLE let tracksFetcher = api.fetchTracks // Background .asObservable()

Slide 49

Slide 49 text

ERROR HANDLING let tracksFetcher = api.fetchTracks // Background .asObservable() .retry(3) .catchErrorJustReturn([])

Slide 50

Slide 50 text

MAPPING let tracksFetcher = api.fetchTracks // Background .asObservable() .retry(3) .catchErrorJustReturn([]) .map(TrackEntity.mapper().map)

Slide 51

Slide 51 text

FILTERING let tracksFetcher = api.fetchTracks // Background .asObservable() .retry(3) .catchErrorJustReturn([]) .map(TrackEntity.mapper().map) .filter { $0.name.contains(query) }

Slide 52

Slide 52 text

FLATMAPPING let tracksFetcher = api.fetchTracks // Background .asObservable() .retry(3) .catchErrorJustReturn([]) .map(TrackEntity.mapper().map) .filter { $0.name.contains(query) } .flatMap { self.rx_trackImage(track: $0) }

Slide 53

Slide 53 text

OBSERVATION THREAD let tracksFetcher = api.fetchTracks // Background .asObservable() .retry(3) .catchErrorJustReturn([]) .map(TrackEntity.mapper().map) .filter { $0.name.contains(query) } .flatMap { self.rx_trackImage(track: $0) } .observeOn(MainScheduler.instance) // Main thread

Slide 54

Slide 54 text

LE IMPERATIVE WAY !" func fetchTracks(retry retry: Int = 0, retryLimit: Int, query: query, mapper: (AnyObject) -> Track, completion: ([UIImage], Error?) -> ()) { api.fetchTracksWithCompletion { (json, error) in if let _ = error where retry < retryLimit { fetchTracksWithRetry(retry: retry+1, retryLimit: retryLimit, query: query, mapper: mapper, completion: completion) } else if let error = error { completion([], error) } else if let json = json { guard let jsonArray = json as? [AnyObject] else { completion([], Error.InvalidResponse) return } let mappedTracks = jsonArray.map(mapper) let filteredTracks = mappedTracks.filter { $0.name.contains(query) } self.fetchImages(track: filteredTracks, completion: ([])) let trackImages = self.fetchImages(tracks: filteredTracks, completion: { (images, error) in dispatch_async(dispatch_get_main_queue(),{ if let error = error { completion([], error) return } completion(images, error) }) }) } } }

Slide 55

Slide 55 text

THROTTLING CLASSIC REACTIVE EXAMPLE func tracksFetcher(query: String) -> Observable<[TrackEntity]> searchTextField .rx_text.throttle(0.5, scheduler: MainScheduler.instance) .flatmap(tracksFetcher) .subscribeNext { tracks in // Yai! Tracks searched }

Slide 56

Slide 56 text

OTHER OPERATORS Combining / Skipping Values / Deferring / Concatenation / Take some values / Zipping

Slide 57

Slide 57 text

OBSERVING ! EVENTS

Slide 58

Slide 58 text

SUBSCRIBING observable subscribe { event switch (event) { case .Next(let value): print(value) case .Completed: print("completed") case .Error(let error): print("Error: \(error)") } }

Slide 59

Slide 59 text

BIND CHANGES OVER THE TIME TO AN OBSERVABLE OBSERVABLE ▶ BINDING ▶ OBSERVER

Slide 60

Slide 60 text

BINDING To a Variable let myVariable: Variable = Variable("") observable .bindTo(myVariable) print(myVariable.value) To UI observable .bindTo(label.rx_text)

Slide 61

Slide 61 text

No content

Slide 62

Slide 62 text

! CAVEATS BECAUSE YES... IT COULDN'T BE PERFECT

Slide 63

Slide 63 text

DEBUGGING DEBUG OPERATOR IN RXSWIFT

Slide 64

Slide 64 text

let tracksFetcher = api.fetchTracks // Background .asObservable() .retry(3) .catchErrorJustReturn([]) .map(TrackEntity.mapper().map) .filter { $0.name.contains(query) } .flatMap { self.rx_trackImage(track: $0) } .observeOn(MainScheduler.instance) // Main thread

Slide 65

Slide 65 text

let tracksFetcher = api.fetchTracks // Background .asObservable .debug("after_fetch") // <-- Debugging probes .retry(3) .catchErrorJustReturn([]) .map(TrackEntity.mapper().map) .debug("mapped_results") // <-- Debugging probes .filter { $0.name.contains(query) } .flatMap { self.rx_trackImage(track: $0) } .observeOn(MainScheduler.instance) // Main thread

Slide 66

Slide 66 text

//let tracksFetcher = api.fetchTracks // Background // .asObservable .debug("after_fetch") // <-- Debugging probes // .retry(3) // .catchErrorJustReturn([]) // .map(TrackEntity.mapper().map) .debug("mapped_results") // <-- Debugging probes // .filter { $0.name.contains(query) } // .flatMap { self.rx_trackImage(track: $0) } // .observeOn(MainScheduler.instance) // Main thread

Slide 67

Slide 67 text

> [after_fetch] Next Event... // Downloaded tracks > [mapped_results] Error ... // Detected error after mapping > [...]

Slide 68

Slide 68 text

RETAIN CYCLES class IssuePresenter { var disposable: Disposable func fetch() { self.disposable = issueTitle .observable() .bindTo { self.titleLabel.rx_text } } }

Slide 69

Slide 69 text

UNSUBSCRIPTION YOU NEED TO TAKE CARE OF THE LIFECYCLE OF YOUR OBSERVABLES

Slide 70

Slide 70 text

UNSUBSCRIPTION title.asObservable().bindTo(field.rx_text) // RxSwift will show a warning

Slide 71

Slide 71 text

UNSUBSCRIPTION let titleDisposable = title.asObservable().bindTo(field.rx_text) titleDisposable.dispose()

Slide 72

Slide 72 text

UNSUBSCRIPTION let disposeBag = DisposeBag() title.asObservable() .bindTo(field.rx_text) .addDisposableTo(disposeBag) // The binding is active until the disposeBag is deallocated

Slide 73

Slide 73 text

CONCLUSIONS

Slide 74

Slide 74 text

PREVENTS STATEFUL CODE

Slide 75

Slide 75 text

DATA FLOW MANIPULATION BECOMES EASIER

Slide 76

Slide 76 text

BUT... !

Slide 77

Slide 77 text

YOU COUPLE YOUR PROJECT TO A LIBRARY !

Slide 78

Slide 78 text

REACTIVE CODE SPREADS LIKE A VIRUS ! OVERREACTIVE ⚠

Slide 79

Slide 79 text

DEFINE REACTIVE DESIGN GUIDELINES AND STICK TO THEM

Slide 80

Slide 80 text

HAVE REACTIVE FUN !

Slide 81

Slide 81 text

REFERENCES > rxmarbles.com > RxSwift Community > RxSwift Repository > ReactiveCocoa

Slide 82

Slide 82 text

WE ARE HIRING [email protected] - [email protected] ❄ BERLIN - BARCELONA !

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

THANKS QUESTIONS? SLIDES HTTP://BIT.LY/1RFWLCI @SAKY - @PEPIBUMUR