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

Reactive Thinking in iOS Development

Reactive Thinking in iOS Development

In a world where Imperative Programming is the most used paradigm, Reactive comes up to make our code more reusable, robust, and stateless. Learn what Functional Reactive Programming means and how it could help you with problems you have to face daily in your projects. We’ll present basic concepts and practical examples for iOS developers that will help you to start thinking in streams, observers, .. and mix them with cool Swift functional concepts.

Pedro Piñera Buendía

May 11, 2016
Tweet

More Decks by Pedro Piñera Buendía

Other Decks in Technology

Transcript

  1. 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
  2. INDEX > Programming Paradigms > Reactive Libraries > Reactive Motivation

    > Reactive Thinking > Reactive Caveats > Conclusion
  3. 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
  4. IMPERATIVE PROGRAMMING func userDidSearch(term: String) { let apiReults = api.search(term:

    term).execute() self.items = self.adaptResults(apiResults) self.tableView.reloadData() }
  5. DECLARATIVE PROGRAMMING let predicate = NSPredicate(format: "name == %@", "Pedro")

    let regex = NSRegularExpression(pattern: ".+", options: 0)
  6. REACTIVE PROGRAMMING DATA-FLOW PROGRAMMING (DESCRIBES THE STATE PROPAGATION) # THE

    INTRODUCTION TO REACTIVE PROGRAMMING THAT YOU'VE BEEN MISSING
  7. REACTIVE LIBRARIES > RxSwift (ReactiveX) > ReactiveCocoa > BrightFutures >

    ReactKit > Bond > More and more... PromiseKit, Bolts...
  8. SWIFT var userName: String { didSet { // React to

    changes in variables view.updateTitle(userName) } }
  9. OPERATIONS RxSwift Observable<String>.create { (observer) -> Disposable in observer.onNext("next value")

    observer.onCompleted() return NopDisposable.instance // For disposing the action } ReactiveCocoa SignalProducer<String>.create { (observer, disposable) in observer.sendNext("next value") observer.sendComplete() }
  10. 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) }
  11. 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
  12. 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) }) }) } } }
  13. 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 }
  14. SUBSCRIBING observable subscribe { event switch (event) { case .Next(let

    value): print(value) case .Completed: print("completed") case .Error(let error): print("Error: \(error)") } }
  15. BINDING To a Variable let myVariable: Variable<String> = Variable("") observable

    .bindTo(myVariable) print(myVariable.value) To UI observable .bindTo(label.rx_text)
  16. 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
  17. 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
  18. //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
  19. RETAIN CYCLES class IssuePresenter { var disposable: Disposable func fetch()

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