reactive thinking in ios development - pedro piñera buendía - codemotion amsterdam 2016

Post on 09-Jan-2017

375 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

REACTIVE THINKING IN IOS DEVELOPMENT

@PEPIBUMUR / @SAKY

WHO?

@pepibumuriOS Developer at SoundCloud

GitHub: pepibumurTwitter: pepibumur

@sakyiOS Developer at Letgo

GitHub: isaacroldanTwitter: saky

GitDo.io our spare time project

INDEX

> Programming Paradigms> Reactive Libraries

> Reactive Motivation> Reactive Thinking> Reactive Caveats

> Conclusion

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

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

IMPERATIVE PROGRAMMINGDECLARATIVE PROGRAMMING

IMPERATIVE PROGRAMMINGDECLARATIVE PROGRAMMING

HOWSEQUENCE OF STEPS

THAT HAPPEN IN ORDER

NATURAL WAY TO PROGRAM

EXECUTION STATE(AKA SIDE EFFECT)

IMPERATIVE PROGRAMMING

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

IMPERATIVE PROGRAMMINGDECLARATIVE PROGRAMMING

WHATIT DOESN'T DESCRIBE THE

CONTROL FLOW

HTML

HTMLSQL

HTMLSQL

REACTIVE PROGRAMMING

DECLARATIVE PROGRAMMING

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

REACTIVE PROGRAMMINGDATA-FLOW PROGRAMMING

(DESCRIBES THE STATE PROPAGATION)

# THE INTRODUCTION TO REACTIVE PROGRAMMING THAT YOU'VE BEEN MISSING

FUNCTIONAL REACTIVE PROGRAMMING

(AKA FRP)

WHAT LIBRARY SHOULD I USE? !

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

SWIFT

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

}}

SWIFT

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

MOTIVATION !WHY SHOULD I STICK TO REACTIVE PROGRAMMING?

> Bindings> Composability

> Threading

> Bindings> Composability

> Threading

BINDING

UI BINDING

UI BINDING

UI BINDING

> Bindings> Composability

> Threading

> Bindings> Composability

> Threading

> Bindings> Composability

> Threading (observer and execution)

THREADING

REACTIVE THINKING !

THINKING IN TERMS OF

OBSERVABLESOR SIGNALS/PRODUCERS IN REACTIVECOCOA

ACTIONS CAN BE

OBSERVEDHOW? !

.NEXT(T).ERROR(ERRORTYPE)

.COMPLETE

OPERATIONS

RxSwiftObservable<String>.create { (observer) -> Disposable in observer.onNext("next value") observer.onCompleted() return NopDisposable.instance // For disposing the action}

ReactiveCocoaSignalProducer<String>.create { (observer, disposable) in observer.sendNext("next value") observer.sendComplete()}

EXISTING PATTERNSRXSWIFT ▶︎ RXCOCOA (EXTENSIONS)

REACTIVECOCOA ▶︎ DO IT YOURSELF

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

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

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

PLAYING !WITH OBSERVABLES

OBSERVABLE

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

ERROR HANDLING

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

MAPPING

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

FILTERING

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

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) }

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

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) }) }) } }}

THROTTLINGCLASSIC 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 }

OTHER OPERATORS

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

OBSERVING !EVENTS

SUBSCRIBING

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

BIND CHANGES OVER THE TIME TO AN OBSERVABLE

OBSERVABLE ▶ BINDING ▶ OBSERVER

BINDING

To a Variablelet myVariable: Variable<String> = Variable("")observable .bindTo(myVariable)print(myVariable.value)

To UIobservable .bindTo(label.rx_text)

! CAVEATSBECAUSE YES...

IT COULDN'T BE PERFECT

DEBUGGINGDEBUG OPERATOR IN RXSWIFT

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

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

//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

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

RETAIN CYCLES

class IssuePresenter { var disposable: Disposable

func fetch() { self.disposable = issueTitle .observable() .bindTo { self.titleLabel.rx_text } }}

UNSUBSCRIPTIONYOU NEED TO TAKE CARE OF THE

LIFECYCLE OF YOUR OBSERVABLES

UNSUBSCRIPTION

title.asObservable().bindTo(field.rx_text)

// RxSwift will show a warning

UNSUBSCRIPTION

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

UNSUBSCRIPTION

let disposeBag = DisposeBag()

title.asObservable() .bindTo(field.rx_text) .addDisposableTo(disposeBag)

// The binding is active until the disposeBag is deallocated

CONCLUSIONS

PREVENTS STATEFUL CODE

DATA FLOW MANIPULATION BECOMES EASIER

BUT... !

YOU COUPLE YOUR PROJECT TO A LIBRARY

!

REACTIVE CODE SPREADS LIKE A VIRUS !

OVERREACTIVE ⚠

DEFINE REACTIVE DESIGN GUIDELINES AND STICK TO

THEM

HAVE REACTIVE FUN !

REFERENCES

> rxmarbles.com> RxSwift Community> RxSwift Repository

> ReactiveCocoa

WE ARE HIRINGPEPI@SOUNDCLOUD.COM - ISAAC@LETGO.COM

❄ BERLIN - BARCELONA !

THANKSQUESTIONS?SLIDES HTTP://BIT.LY/1RFWLCI

@SAKY - @PEPIBUMUR

top related