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

Reactive programming from Scratch

Reactive programming from Scratch

Slides from my talk at UIKonf 2017, May 15th

Reactive programming is an exciting approach to building responsive, interactive and robust apps. But it also comes at a cost: the learning curve is steep and can be long. Because of this, getting started with reactive programming can seem to be a daunting task.

With this talk, I want to give the audience the confidence to get started with reactive programming. I want to take away any (perceived) magic by focussing on the core concepts: observables, observers and subscriptions. I will explain these concepts by writing a tiny reactive library in only a few dozen lines of code.

Thomas Visser

May 15, 2017
Tweet

More Decks by Thomas Visser

Other Decks in Technology

Transcript

  1. !

  2. getRaceResult() .prefix(3) .enumerated() .map { offset, elem in return "\(offset+1).

    \(elem)" } .joined(separator: "\n") // 1. Bolt // 2. De Grasse // 3. Lemaitre
  3. var athletes: [String] = [] var res: String? = nil

    race.onAthleteDidFinish { athlete in if athletes.count < 3 { athletes.append("\(athletes.count + 1). \(athlete)") } else if res == nil { res = athletes.joined(separator: "\n") } } // 1. Bolt // 2. De Grasse // 3. Lemaitre
  4. button.addTarget(self, action: #selector(buttonTap), for: [.touchUpInside]) // [tap, tap tap] let

    manager = CLLocationManager() manager.delegate = self // [(52.47695990, 13.43936600), (52.51716100, 13.44998000)] URLSession.shared.dataTask(with: URL(string: "http://www.uikonf.com")!) { d, r, e in // ["<html><head><title>UIKonf</title>..."] }.resume()
  5. public protocol Sequence { associatedtype Iterator : IteratorProtocol public func

    makeIterator() -> Self.Iterator } public protocol IteratorProtocol { associatedtype Element public mutating func next() -> Self.Element? }
  6. var z = 0 let s = AnyIterator { ()

    -> Int in defer { z += 1 } return z }
  7. var z = 0 let s = AnyIterator { ()

    -> Int in defer { z += 1 } return z } for i in s { print(i) } // prints: // 0 // 1 // 2 // 3 // etc.
  8. var z = 0 let s = AnyIterator { ()

    -> Int in defer { z += 1 } return z } for i in s.prefix(3) { print(i) } // prints: // 0 // 1 // 2
  9. var z = 0 let s = AnyIterator { ()

    -> Int in defer { z += 1 } return z } for i in s.lazy.map({ $0 % 2 == 0 }) { print(i) } // prints: // true // false // true // etc.
  10. class AsyncSequence<E>: Sequence { func makeIterator() -> AnyIterator<E> { return

    AnyIterator { while noNextValueAvailable { self.semaphore.wait() } return nextValue } } }
  11. class Observable<E> { var values: [E] init(values: [E]) { self.values

    = values } func subscribe(_ observer: Observer<E>) { for v in values { observer(v) } } }
  12. class Observable<E> { var values: [E] init(values: [E]) { self.values

    = values } func subscribe(_ observer: Observer<E>) { for v in values { observer(v) } } }
  13. class Observable<E> { var values: [E] var observers: [Observer<E>] =

    [] init(values: [E]) { self.values = values } func subscribe(_ observer: @escaping Observer<E>) { observers.append(observer) for v in values { observer(v) } } func append(_ newElement: E) { values.append(newElement) for o in observers { o(newElement) } } }
  14. let o = Observable<Int>(values: [1, 2, 3]) o.subscribe { print($0)

    } o.append(4) // prints: // 1 // 2 // 3 // 4
  15. class Observable<E> { var values: [E] var observers: [Observer<E>] =

    [] init(values: [E]) { self.values = values } func subscribe(_ observer: @escaping Observer<E>) { observers.append(observer) for v in values { observer(v) } } func append(_ newElement: E) { values.append(newElement) for o in observers { o(newElement) } } }
  16. class Observable<E> { var observers: [Observer<E>] = [] func subscribe(_

    observer: @escaping Observer<E>) { observers.append(observer) } func append(_ newElement: E) { for o in observers { o(newElement) } } }
  17. class Observable<E> { var observers: [Observer<E>] = [] func subscribe(_

    observer: @escaping Observer<E>) { observers.append(observer) } func append(_ newElement: E) { for o in observers { o(newElement) } } }
  18. class Observable<E> { typealias SubscriptionHandler = (@escaping Observer<E>) -> Void

    let handler: SubscriptionHandler init(subscriptionHandler: @escaping SubscriptionHandler) { self.handler = subscriptionHandler } func subscribe(_ observer: @escaping Observer<E>) { self.handler(observer) } }
  19. let o = Observable<Int> { obs in obs(1) obs(2) obs(3)

    obs(4) } o.subscribe { print($0) } // prints: // 1 // 2 // 3 // 4
  20. let o = Observable<Int> { obs in obs(1) obs(2) obs(3)

    obs(4) } o.subscribe { } // -------- let s = [1, 2, 3, 4] for elem in s { } s.forEach { }
  21. let o = Observable<Int> { obs in obs(1) dispatchQueue.asyncAfter(deadline: .now()

    + .seconds(1)) { obs(2) } } o.subscribe { print($0) } // prints: // 1 // 2
  22. func timer(delay: Int) -> Observable<Int> { return Observable { obs

    in DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(delay)) { obs(delay) } } } //
  23. func timer(delay: Int) -> Observable<Int> { return Observable { obs

    in DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(delay)) { obs(delay) } } } timer(delay: 360).subscribe { _ in print("Your soft boiled egg is ready!") }
  24. func response(url: URL) -> Observable<(Data, URLResponse)> { return Observable {

    obs in URLSession.shared.dataTask(with: url) { d, r, e in guard let data = d, let response = r else { return } obs((data, response)) }.resume() } }
  25. extension Observable { func same() -> Observable<E> { return Observable

    { observer in self.subscribe { elem in observer(elem) } } } }
  26. extension Observable where E == Int { func plusOne() ->

    Observable<Int> { return Observable { observer in self.subscribe { elem in observer(elem + 1) } } } }
  27. extension Observable { func map<U>(_ f: @escaping (E) -> U)

    -> Observable<U> { return Observable<U> { observer in self.subscribe { elem in observer(f(elem)) } } } }
  28. let o = Observable<Int> { obs in obs(1) obs(2) obs(3)

    } let o1 = o.plusOne().map { $0 % 2 == 0 } o1.subscribe { print($0) } //
  29. let o = Observable<Int> { obs in obs(1) obs(2) obs(3)

    } let o1 = o.plusOne().map { $0 % 2 == 0 } o1.subscribe { print($0) } o1.subscribe { print($0) }
  30. let uikonf = response(url: URL(string: "http://www.uikonf.com")!) uikonf.subscribe { data, response

    in print(data) } uikonf.subscribe { data, response in print(data) }
  31. extension Observable { func share() -> Observable<E> { var subscribed

    = false return Observable<E> { observer in if !subscribed { self.subscribe { e in } subscribed = true } } } }
  32. extension Observable { func share() -> Observable<E> { var subscribed

    = false var observers: [Observer<E>] = [] return Observable<E> { observer in observers.append(observer) if !subscribed { self.subscribe { e in for o in observers { o(e) } } subscribed = true } } } }
  33. //                                                                               class Observable<E> { typealias SubscriptionHandler = (@escaping Observer<E>)

    -> Void let handler: SubscriptionHandler init(subscriptionHandler: @escaping SubscriptionHandler) { self.handler = subscriptionHandler } func subscribe(_ observer: @escaping Observer<E>) { self.handler(observer) } }
  34. typealias Disposable = () -> Void                                                                               class Observable<E> {

    typealias SubscriptionHandler = (@escaping Observer<E>) -> (Disposable) let handler: SubscriptionHandler init(subscriptionHandler: @escaping SubscriptionHandler) { self.handler = subscriptionHandler } func subscribe(_ observer: @escaping Observer<E>) -> Disposable { return self.handler(observer) } }
  35. func response(url: URL) -> Observable<(Data, URLResponse)> { return Observable {

    observer in let t = URLSession.shared.dataTask(with: url) { d, r, e in guard let data = d, let response = r else { return } observer((d, r)) } t.resume() return { t.cancel() } } }
  36. class View { var disposable: Disposable? func bind(to viewModel: ViewModel)

    { disposable = viewModel.title().subscribe { [weak self] in self.titleLabel.text = $0 } } deinit { disposable?() } }
  37. class Observable<E> { var observers: [Observer<E>] = [] func append(_

    newElement: E) { for o in observers { o(newElement) } } func subscribe(_ observer: @escaping Observer<E>) { observers.append(observer) } }
  38. class Subject<E>: Observable<E> { let observers: [Observer<E>] = [] init()

    { super.init { observer in observers.append(observer) return { observers.remove(observer) } } } func send(_ newElement: E) { for o in observers { o(newElement) } } }
  39. class Subject<E>: Observable<E> { let observers: Box<[Observer<E>?]> init() { let

    observers = Box<[Observer<E>?]>([]) self.observers = observers super.init { observer in let i = observers.value.count observers.value.append(observer) return { observers.value[i] = nil } } } func send(_ newElement: E) { for o in observers.value { o?(newElement) } } }
  40.                                                              class ViewController { let buttonTaps = Subject<Void>() func submitButtonTapped()

    { buttonTaps.send() } init() { let d = buttonTaps.subscribe { let url = URL(string: "http://www.uikonf.com")! let d1 = response(url: url).subscribe { d, r in print(d) } } } }
  41.                                                              class ViewController { let buttonTaps = Subject<Void>() func submitButtonTapped()

    { buttonTaps.send() } init() { let d = buttonTaps.map { _ in let url = URL(string: "http://www.uikonf.com")! return response(url: url) }.subscribe { // recieves Observable<(Data, URLResponse)> } } }
  42. extension Observable where E == ObservableProtocol { func flatten() ->

    Observable<E.Element> { // ... } } protocol ObservableProtocol { associatedtype Element func subscribe(_ observable: @escaping Observer<Element>)    -> Disposable } extension Observable: ObservableProtocol { }
  43. extension Observable where E == ObservableProtocol { func flatten() ->

    Observable<E.Element> { return Observable<E.Element> { observer in self.subscribe { innerObservable in innerObservable.subscribe(observer)              } return { } } } }
  44. extension Observable where E == ObservableProtocol { func flatten() ->

    Observable<E.Element> { return Observable<E.Element> { observer in var disposables: [Disposable] = [] let outerD = self.subscribe { innerObservable in let innerD = innerObservable.subscribe(observer) disposables.append(innerD) } disposables.append(outerD) return { for d in disposables { d() } } } } }
  45. class ViewController { let buttonTaps = Subject<Void>() func submitButtonTapped() {

    buttonTaps.send() } init() { let d = buttonTaps.map { _ in let url = URL(string: "http://www.uikonf.com")! return response(url: url) }.flatten().subscribe { data, response in print(data) } } }
  46. ✅ Observer ✅ Observable ✅ Operators ✅ Subscribing & Disposing

    ✅ Sharing side-effects ✅ Subject ✅ Combining Observables
  47. The introduction to Reactive Programming you've been missing by @andrestaltz

    (https:// gist.github.com/staltz/868e7e9bc2a7b8c1f754) Reactive Programming Workshop during the Unconference Day RxSwift, ReactiveSwift, ReactKit