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

Conquering ReactiveSwift

Conquering ReactiveSwift

I used these slides in my talk at iOSDevUK 2019.
This talk walks you through different primitives of ReactiveSwift.

Avatar for Susmita Horrow

Susmita Horrow

September 03, 2019

Other Decks in Programming

Transcript

  1. > < next previous t1 “h” t2 “he” t3 “hel”

    t4 “hell” t5 “hello” label.text = textfield.text label.text = textfield.text label.text = textfield.text label.text = textfield.text label.text = textfield.text IMPERATIVE
  2. > < next previous t1 “h” t2 “he” t3 “hel”

    t4 “hell” t5 “hello” REACTIVE label.reactive.text <~ textField.reactive.continuousTextValues.skipNil()
  3. > < next previous Source Consumer Scope Operators Observer Mutable

    Property Side Effects Binding Transform Combine Flatten Lifetime Disposable Property Event Signal Signal Producer Action PRIMITIVES
  4. > < next previous let observer: Observer<Bool, Never> = Observer(value:

    { value in button.isEnabled = value }) textField.reactive.continuousTextValues.skipNil() let disposable = .map { text in text.characters.count > 0 } .observe(observer) disposable?.dispose() SOURCE CONSUMER SCOPE OPERATOR
  5. > < next previous OBSERVER SIGNAL EVENT DISPOSABLE SIGNALPRODUCER BINDING

    MUTABLEPROPERTY LIFETIME PROPERTY ACTION OPERATORS MILESTONE
  6. > < next previous EVENT The basic unit ‣ Completed

    ‣ Failed ‣ Interrupted ‣ Value
  7. > < next previous OBSERVER The responder to the stream

    of events Encapsulates the work to be performed in response to the events emitted
  8. > < next previous let observer = Signal<Int, Never>.Observer {

    (event) in switch event { case let .value(v): print("value = \(v)”) case let .failed(error): print("error = \(error)”) case .completed: print(“completed") case .interrupted: print("interrupted") } }
  9. > < next previous Print a message of time elapsed

    on every 5 seconds interval for 50 seconds
  10. > < next previous let (output, input) = Signal<Int, Never>.pipe()

    Observer<Int, Never> INPUT Signal<Int, Never> OUTPUT
  11. > < next previous let (output, input) = Signal<Int, Never>.pipe()

    for i in 0..<10 { } input.send(value: timeElapsed) DispatchQueue.main.asyncAfter(deadline: .now() + Double(timeElapsed)) { } let timeElapsed = index * 5
  12. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer
  13. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } )
  14. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } ) let (output, input) = Signal<Int, Never>.pipe() for i in 0..<10 { let timeElapsed = index * 5 DispatchQueue.main.asyncAfter( deadline: .now() + Double(timeElapsed)) { input.send(value: timeElapsed) } }
  15. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } ) let (output, input) = Signal<Int, Never>.pipe() for i in 0..<10 { let timeElapsed = index * 5 DispatchQueue.main.asyncAfter( deadline: .now() + Double(timeElapsed)) { input.send(value: timeElapsed) } } output.observe(signalObserver)
  16. > < next previous OBSERVER SIGNAL EVENT DISPOSABLE SIGNALPRODUCER BINDING

    MUTABLEPROPERTY LIFETIME PROPERTY ACTION OPERATORS MILESTONE
  17. > < next previous SIGNALPRODUCER A deferred and repeatable work

    that creates a stream of values 6 5 4 3 2 1 6 5 4 3 2 1 6 5 4 3 2 1 SIGNALPRODUCER START START START
  18. > < next previous Print a message of time elapsed

    on every 5 seconds interval for 50 seconds
  19. > < next previous Print a message of time elapsed

    on every 5 seconds interval for 50 seconds which starts on a button tap
  20. > < next previous let signalProducer: SignalProducer<Int, Never> = SignalProducer

    { (observer, lifetime) in } for i in 0..<10 { } if i == 9 { //Mark completion on 9th iteration observer.sendCompleted() } observer.send(value: timeElapsed) } DispatchQueue.main.asyncAfter(deadline: .now() + Double(timeElapsed)) { let timeElapsed = index * 5
  21. > < next previous ‣ Start the signalProducer ‣ Create

    the signalProducer ‣ Create the observer
  22. > < next previous ‣ Start the signalProducer ‣ Create

    the signalProducer ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } )
  23. > < next previous ‣ Start the signalProducer ‣ Create

    the signalProducer ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } ) let signalProducer: SignalProducer<Int, Never> = SignalProducer { (observer, lifetime) in for i in 0..<10 { let timeElapsed = index * 5 DispatchQueue.main.asyncAfter( deadline: .now() + Double(timeElapsed)) { observer.send(value: timeElapsed) if i == 9 { observer.sendCompleted() } } } }
  24. > < next previous ‣ Start the signalProducer ‣ Create

    the signalProducer ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } ) let signalProducer: SignalProducer<Int, Never> = SignalProducer { (observer, lifetime) in for i in 0..<10 { let timeElapsed = index * 5 DispatchQueue.main.asyncAfter( deadline: .now() + Double(timeElapsed)) { observer.send(value: i) if i == 9 { observer.sendCompleted() } } } } signalProducer.start(signalObserver)
  25. > < next previous DISPOSABLE & LIFETIME Represents something that

    can be “disposed”, usually associated with freeing resources or canceling work. let disposable: Disposable? = signal.observe(signalObserver) disposable.dispose()
  26. > < next previous DISPOSABLE & LIFETIME Represents something that

    can be “disposed”, usually associated with freeing resources or canceling work. disposable.dispose() let disposable: Disposable? = signalProducer.start(signalObserver)
  27. > < next previous let signalProducer: SignalProducer<Int, Never> = SignalProducer

    { (observer, lifetime) in } for i in 0..<10 { } if i == 9 { observer.sendCompleted() } observer.send(value: timeElapsed) } DispatchQueue.main.asyncAfter(deadline: .now() + Double(timeElapsed)) { let timeElapsed = index * 5
  28. > < next previous let signalProducer: SignalProducer<Int, Never> = SignalProducer

    { (observer, lifetime) in } for i in 0..<10 { } if i == 9 { observer.sendCompleted() } observer.send(value: timeElapsed) } DispatchQueue.main.asyncAfter(deadline: .now() + Double(timeElapsed)) { let timeElapsed = index * 5 Lifetime of the produced signal
  29. > < next previous let signalProducer: SignalProducer<Int, Never> = SignalProducer

    { (observer, lifetime) in for i in 0..<10 { let timeElapsed = index * 5 DispatchQueue.main.asyncAfter(deadline: .now() + Double(timeElapsed)) { guard !lifetime.hasEnded else { observer.sendInterrupted() return } observer.send(value: timeElapsed) if i == 9 { observer.sendCompleted() } } } }
  30. > < next previous SIGNAL SIGNALPRODUCER ‣ Like video stream

    in TV ‣ Like on demand stream in Youtube ‣ Sequence of stream is same for all observer ‣ Sequence of stream is different for different observer ‣ Observer cannot interrupt the stream ‣ Observer can interrupt the stream ‣ Can represent event streams that are already “in progress,” such as notifications, user input ‣ Can represent operations or tasks which need to be started such as network requests
  31. > < next previous OBSERVER SIGNAL EVENT DISPOSABLE SIGNALPRODUCER BINDING

    MUTABLEPROPERTY LIFETIME PROPERTY ACTION OPERATORS MILESTONE LIFETIME
  32. > < next previous PROPERTY 6 5 4 3 2

    1 .signal .producer 6 5 4 3 2 1 START .value 1
  33. > < next previous let signalProducer: SignalProducer<Int, Never> = SignalProducer

    { (observer, lifetime) in for i in 0..<10 { DispatchQueue.main.asyncAfter(deadline: .now() + 5.0 * Double(i)) { guard !lifetime.hasEnded else { observer.sendInterrupted() return } observer.send(value: timeElapsed) if i == 9 { observer.sendCompleted() } } } }
  34. > < next previous let property = Property(initial: 0, then:

    signalProducer) property.producer.startWithValues { value in print("[Observing SignalProducer] Time elapsed = \(value)") } property.signal.observeValues { value in print("[Observing Signal] Time elapsed = \(value)") }
  35. > < next previous MUTABLEPROPERTY An observable and mutable box

    that always holds a value let mutableProperty = MutableProperty(1) mutableProperty.value = 3
  36. > < next previous OBSERVER SIGNAL EVENT DISPOSABLE SIGNALPRODUCER BINDING

    MUTABLEPROPERTY LIFETIME PROPERTY ACTION OPERATORS MILESTONE
  37. > < next previous ACTION A serialized deferred and repeatable

    work with a preset action. ‣ Deferred ‣ Repeatable ‣ Mutually exclusive ‣ Varying input Like SignalProducer Unlike SignalProducer
  38. > < next previous Print a message of time elapsed

    on every 5 seconds interval for 50 seconds which starts on a button tap
  39. > < next previous Print a message of time elapsed

    on every N seconds interval for N * 10 seconds which starts on a button tap
  40. > < next previous DispatchQueue.main.asyncAfter(deadline: .now() * Double(timeElapsed)) { guard

    !lifetime.hasEnded else { observer.sendInterrupted() return } observer.send(value: timeElapsed) if i == 9 { observer.sendCompleted() } } let signalProducer: SignalProducer<Int, Never> = SignalProducer { (observer, lifetime) in } for i in 0..<10 { } let timeElapsed = index * 5
  41. > < next previous DispatchQueue.main.asyncAfter(deadline: .now() * Double(timeElapsed)) { guard

    !lifetime.hasEnded else { observer.sendInterrupted() return } observer.send(value: timeElapsed) if i == 9 { observer.sendCompleted() } } let signalProducerGenerator: (Int) -> SignalProducer<Int, NoError> = { timeInterval in return SignalProducer { (observer, lifetime) } for i in 0..<10 { } let timeElapsed = index * timeInterval
  42. > < next previous DispatchQueue.main.asyncAfter(deadline: .now() * Double(timeElapsed)) { guard

    !lifetime.hasEnded else { observer.sendInterrupted() return } observer.send(value: timeElapsed) if i == 9 { observer.sendCompleted() } } let signalProducerGenerator: (Int) -> SignalProducer<Int, NoError> = { timeInterval in return SignalProducer { (observer, lifetime) } for i in 0..<10 { } let timeElapsed = index * timeInterval
  43. > < next previous let signalProducer1 = signalProducerGenerator(1) let signalProducer2

    = signalProducerGenerator(2) signalProducer1.startWithValues { value in print("value from signalProducer1 = \(value)") } signalProducer2.startWithValues { value in print("value from signalProducer2 = \(value)") }
  44. > < START 6 5 4 3 2 1 6

    5 4 3 2 1 6 5 4 3 2 1 SIGNALPRODUCER START START APPLY ACTION next previous
  45. > < next previous ACTION ‣ values Signal<Output, Never> ‣

    errors Signal<Error, Never> ‣ isExecuting Property<Bool> ‣ isEnabled Property<Bool>
  46. > < next previous public final class Action<Input, Output, Error:

    Swift.Error> {} ACTION Type of input Type of values emitted Type of error emitted
  47. > < next previous ‣ Apply the action ‣ Create

    the observer ‣ Create the action
  48. > < next previous ‣ Apply the action ‣ Create

    the observer ‣ Create the action let action = Action<(Int), Int, Never> (execute: signalProducerGenerator)
  49. > < next previous ‣ Apply the action ‣ Create

    the observer ‣ Create the action let action = Action<(Int), Int, Never> (execute: signalProducerGenerator) action.values.observeCompleted { print("Action completed") } action.values.observeValues { value in print("Time elapsed = \(value)") }
  50. > < next previous ‣ Apply the action ‣ Create

    the observer ‣ Create the action let action = Action<(Int), Int, Never> (execute: signalProducerGenerator) action.values.observeCompleted { print("Action completed") } action.values.observeValues { value in print("Time elapsed = \(value)") } action.apply(1).start() action.apply(2).start() DispatchQueue.main.asyncAfter(deadline: .now() + 12.0){ action.apply(3).start() }
  51. > < next previous OBSERVER SIGNAL EVENT DISPOSABLE SIGNALPRODUCER BINDING

    MUTABLEPROPERTY LIFETIME PROPERTY ACTION OPERATORS MILESTONE LIFETIME
  52. > < next previous BINDING Modelling behaviour in response to

    state change label.reactive.text <~ textField.reactive.continuousTextValues.skipNil()
  53. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } ) let (output, input) = Signal<Int, Never>.pipe() for i in 0..<10 { DispatchQueue.main.asyncAfter( deadline: .now() + 5.0 * Double(i)) { input.send(value: timeElapsed) } } output.observe(signalObserver)
  54. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let signalObserver = Observer<Int, Never> ( value: { value in print("Time elapsed = \(value)") }, completed: { print("completed") }, interrupted: { print("interrupted") } ) let (output, input) = Signal<Int, Never>.pipe() for i in 0..<10 { DispatchQueue.main.asyncAfter( deadline: .now() + 5.0 * Double(i)) { input.send(value: timeElapsed) } } output.observe(signalObserver)
  55. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let (output, input) = Signal<Int, Never>.pipe() for i in 0..<10 { DispatchQueue.main.asyncAfter( deadline: .now() + 5.0 * Double(i)) { input.send(value: timeElapsed) } } output.observe(signalObserver) Create the BindingTarget let signalObserver = BindingTarget<Int>( lifetime: self.reactive.lifetime) { value in print("Time elapsed = \(value)") }
  56. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let (output, input) = Signal<Int, Never>.pipe() for i in 0..<10 { DispatchQueue.main.asyncAfter( deadline: .now() + 5.0 * Double(i)) { input.send(value: timeElapsed) } } output.observe(signalObserver) Create the BindingTarget let signalObserver = BindingTarget<Int>( lifetime: self.reactive.lifetime) { value in print("Time elapsed = \(value)") }
  57. > < next previous ‣ Observe the signal ‣ Create

    the signal ‣ Create the observer let (output, input) = Signal<Int, Never>.pipe() for i in 0..<10 { DispatchQueue.main.asyncAfter( deadline: .now() + 5.0 * Double(i)) { input.send(value: timeElapsed) } } signalObserver <~ output Create the BindingTarget let signalObserver = BindingTarget<Int>( lifetime: self.reactive.lifetime) { value in print("Time elapsed = \(value)") } Bind the signal
  58. > < next previous OBSERVER SIGNAL EVENT DISPOSABLE SIGNALPRODUCER BINDING

    MUTABLEPROPERTY LIFETIME PROPERTY ACTION OPERATORS MILESTONE LIFETIME
  59. > < next previous Side Effects observe on Transform filterMap

    lazyMap mapError map filter Aggregate collect scan reduce Limit skip take skipRepeats uniqueValues Binding <~ skipNil debounce throttle delay or and negate Logical OPERATORS
  60. > < next previous Side Effects observe on Transform filterMap

    lazyMap mapError map filter Aggregate collect scan reduce Limit skip take skipRepeats uniqueValues Binding <~ skipNil debounce throttle delay or and negate Logical OPERATORS
  61. > < next previous Combine Flatten combineLatest combinePrevious zip merge

    concat latest flatMap Signal Producer then first retry repeat single last wait replayLazily Misc. materialize dematerialize sample timeout attempt attemptMap promoteError OPERATORS