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

Be Proactive: Use Reactive

Be Proactive: Use Reactive

What is “functional reactive programming?” Why should you use it? This talk is a high-level, easy-to-grasp intro to the core concepts behind reactive programming.

A video of this talk is available: https://vimeo.com/125191686

And speakers' notes, etc. are on my blog: https://www.dzombak.com/talks/2015-04-be-proactive-use-reactive.html

Chris Dzombak

April 16, 2015
Tweet

More Decks by Chris Dzombak

Other Decks in Programming

Transcript

  1. BE PROACTIVE
    USE
    REACTIVE

    View Slide

  2. iOS @ NY Times
    @cdzombak
    github.com/cdzombak
    chris dzombak
    [email protected]

    View Slide

  3. What is
    reactive programming?

    View Slide

  4. “ ”
    reactive |rēˈaktiv|
    … a programming
    paradigm oriented
    around data flows and
    the propagation of
    change

    View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. reactive programming
    data flows
    propagation of change
    =

    View Slide

  10. View Slide

  11. we all
    respond to flowing data
    and
    react to changes
    Model
    View
    Controller

    View Slide

  12. Cocoa’s KVO?
    NSNotificationCenter?
    Ember or Angular observers?
    Callbacks?
    Listeners?
    How is this different from…
    Programming?

    View Slide

  13. Back to the basics

    View Slide

  14. Subject
    Observer
    notify()

    View Slide

  15. DATA STREAMS

    View Slide

  16. Data type
    a value over time

    View Slide

  17. •updated property values
    •mouse cursor position
    •typing characters into a text field
    •database records

    View Slide

  18. •updated property values
    •mouse cursor position
    •typing characters into a text field
    •database records

    View Slide

  19. Collection of emitted events
    ordered in time

    View Slide

  20. with functions
    for observation

    View Slide

  21. View Slide

  22. onNext( )

    View Slide

  23. onNext( )

    View Slide

  24. onNext( )

    View Slide

  25. onComplete()

    View Slide

  26. View Slide

  27. onNext( )

    View Slide

  28. onNext( )

    View Slide

  29. onError()
    x

    View Slide

  30. [ "value”, "other value”, "another value” ]

    View Slide

  31. [ "value”, "other value”, "another value” ]
    "a value",

    View Slide

  32. [ "value”, "other value”, "another value” ]
    "a value", "other value",

    View Slide

  33. [ "value”, "other value”, "another value” ]
    "a value", "other value", "another value"

    View Slide

  34. textField.rac_textSignal()
    .subscribeNext({ (value) -> Void in
    // value = “H”, “He”, …
    })

    View Slide

  35. textField.rac_textSignal()
    .subscribeNext({ (value) -> Void in
    // value = “H”, “He”, …
    })

    View Slide

  36. apiClient.performRequest(request)
    .subscribeNext({ (response) -> Void in
    // do something with response
    },
    error: { (error) -> Void in
    println(“Error: \(error)”)
    },
    completed: { () -> Void in
    println(“Done.”)
    })

    View Slide

  37. Stream.transform(f: (x) -> y) -> Stream
    Streams are composable

    View Slide

  38. 1 3 2
    2 6 4
    map { (x) -> Int in x * 2 }
    Streams are composable

    View Slide

  39. 1 2 4
    filter { (x) -> Bool in x % 2 == 0 }
    4
    2
    Streams are composable

    View Slide

  40. Double tap!
    Demo

    View Slide

  41. View Slide

  42. View Slide

  43. taps
    .bufferWithTime(0.5)
    .map { ($0 as RACTuple).count }
    .filter { ($0 as Int) == 2 }
    .subscribeNext { (_) in
    println("Double tap!")
    }

    View Slide

  44. taps
    .bufferWithTime(0.5)
    .map { ($0 as RACTuple).count }
    .filter { ($0 as Int) == 2 }
    .subscribeNext { (_) in
    println("Double tap!")
    }

    View Slide

  45. taps
    .bufferWithTime(0.5)
    .map { ($0 as RACTuple).count }
    .filter { ($0 as Int) == 2 }
    .subscribeNext { (_) in
    println("Double tap!")
    }

    View Slide

  46. 1 2 3
    taps
    .bufferWithTime(0.5)
    .map { ($0 as RACTuple).count }
    .filter { ($0 as Int) == 2 }
    .subscribeNext { (_) in
    println("Double tap!")
    }

    View Slide

  47. 2
    taps
    .bufferWithTime(0.5)
    .map { ($0 as RACTuple).count }
    .filter { ($0 as Int) == 2 }
    .subscribeNext { (_) in
    println("Double tap!")
    }

    View Slide

  48. taps
    .bufferWithTime(0.5)
    .map { ($0 as RACTuple).count }
    .filter { ($0 as Int) == 2 }
    .subscribeNext { (_) in
    println("Double tap!")
    }
    Double tap!

    View Slide

  49. RACSignal.combineLatest([
    slowToEmitStream,
    evenSlowerToEmitStream,
    ])
    .doNext {
    let tuple = $0 as RACTuple
    processResults(
    tuple.first,
    tuple.second)
    }
    .deliverOn(RACScheduler.mainThreadScheduler())
    .subscribeCompleted { () -> Void in
    println("The work is done!")
    }

    View Slide

  50. MODEL WITH STREAMS

    View Slide

  51. MODEL WITH STREAMS
    BUT…WHY?

    View Slide

  52. Claim
    code should communicate intent

    View Slide

  53. Current tooling
    is insufficient

    View Slide

  54. map
    extension Array {
    func map(transform: (T) -> U) -> [U]
    }

    View Slide

  55. let input = [1, 2, 3]
    var output = [Int]()
    for number in input {
    output.append(number * 2)
    }
    // output: [2, 4, 6]
    let output = [1, 2, 3].map { $0 * 2 }
    // output: [2, 4, 6]

    View Slide

  56. let input = [1, 2, 3]
    var output = [Int]()
    for number in input {
    output.append(number * 2)
    }
    // output: [2, 4, 6]
    let output = [1, 2, 3].map { $0 * 2 }
    // output: [2, 4, 6]
    Concept: 1-1 mapping

    View Slide

  57. map
    reifies
    1-1 data transformation

    View Slide

  58. Good Thing™
    reify |ˈrēəәˌfī|
    make (something abstract)
    more concrete or real

    View Slide

  59. reactive programming
    reifies
    event-driven software

    View Slide

  60. View Slide

  61. View Slide

  62. var count = 0
    upButton.addTarget(self, action: “upButtonTouched:”,
    forControlEvents: .TouchUpInside)
    downButton.addTarget(self, action: “downButtonTouched:",
    forControlEvents: .TouchUpInside)
    countLabel.text = String(count)
    func upButtonTouched(sender: UIButton) {
    count++
    countLabel.text = String(count)
    }
    func downButtonTouched(sender: UIButton) {
    count--
    countLabel.text = String(count)
    }

    View Slide

  63. let upTaps = upButton
    .rac_signalForControlEvents(.TouchUpInside)
    let downTaps = downButton
    .rac_signalForControlEvents(.TouchUpInside)
    let count = RACSignal.merge([
    RACSignal.return(0),
    upTaps.mapReplace(1),
    downTaps.mapReplace(-1)
    ])
    .scanWithStart(0, reduce: { $0 + $1 })
    .map { String($0) }
    count ~> RAC(self, "countLabel.text")

    View Slide

  64. client.loginWithSuccess({
    client.loadCachedTweetsWithSuccess({ (tweets) in
    client.fetchTweetsAfterTweet(tweets.last,
    success: { (tweets) -> Void in
    // Now we can show our tweets
    },
    failure: { (error) -> Void in
    presentError(error)
    })
    },
    failure: { (error) -> Void in
    presentError(error)
    })
    },
    failure: { (error) -> Void in
    presentError(error)
    })

    View Slide

  65. client.login()
    .then {
    return client.loadCachedTweets()
    }
    .flattenMap { (tweets) -> RACStream in
    return client.fetchTweetsAfterTweet(tweets.last)
    }
    .subscribeError({ (error) -> Void in
    presentError(error)
    },
    completed: { () -> Void in
    // Now we can show our tweets
    })

    View Slide

  66. bring
    changes over time
    into your type system

    View Slide

  67. /// Clients should observe via property
    observer:
    public var title: String
    public let title: Signal

    View Slide

  68. func loginWithSuccess(
    success: () -> Void,
    failure: (NSError) -> Void)
    {
    // function doesn’t return anything
    // it jumps into two callbacks based on return
    }
    func login() -> RACSignal {
    // function has a native return value
    // a data stream object that we can observe
    }

    View Slide

  69. becomes easier
    concurrency

    View Slide

  70. Scheduler 

    schedules when and where work is performed


    reactive concurrency

    View Slide

  71. Scheduler 

    schedules when and where work is performed


    deliverOn(Scheduler)
    subscribeOn(Scheduler)
    reactive concurrency

    View Slide

  72. SO…
    HOW DO I DO THIS?
    (photo by Andrew Sardone)

    View Slide

  73. ReactiveCocoa
    ReactiveExtensions
    RxJava
    Bacon.js
    Elm
    github.com/ReactiveCocoa/ReactiveCocoa
    github.com/Reactive-Extensions
    github.com/ReactiveX/RxJava
    github.com/baconjs/bacon.js
    elm-lang.org

    View Slide

  74. SOURCES
    FURTHER READING

    View Slide

  75. Missing RX Intro
    Input & Output
    RxMarbles
    Other
    j.mp/missingrxintro
    j.mp/joshaberio
    rxmarbles.com
    pinboard.in/u:cdzombak/t:reactiveprogramming

    View Slide

  76. B
    Y
    E
    (photo by Chris Dzombak)
    (puppy by Shane Bliemaster)

    View Slide