Slide 1

Slide 1 text

Reactive programming from scratch        @thomvis

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

let result: [String] = getRaceResult() // ["Bolt", "De Grasse", "Lemaitre", "Gemili", "Martina"]

Slide 6

Slide 6 text

race.onAthleteDidFinish { athlete in // athlete is Bolt, De Grasse, Lemaitre, Gemili and Martina }

Slide 7

Slide 7 text

["Bolt", "De Grasse", "Lemaitre", "Gemili", "Martina"]

Slide 8

Slide 8 text

Everyting is a Sequence

Slide 9

Slide 9 text

!

Slide 10

Slide 10 text

getRaceResult() .prefix(3) .enumerated() .map { offset, elem in return "\(offset+1). \(elem)" } .joined(separator: "\n") // 1. Bolt // 2. De Grasse // 3. Lemaitre

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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 // ["UIKonf..."] }.resume()

Slide 13

Slide 13 text

for elem in seq { }

Slide 14

Slide 14 text

public protocol Sequence { associatedtype Iterator : IteratorProtocol public func makeIterator() -> Self.Iterator } public protocol IteratorProtocol { associatedtype Element public mutating func next() -> Self.Element? }

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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.

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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.

Slide 19

Slide 19 text

class AsyncSequence: Sequence { func makeIterator() -> AnyIterator { return AnyIterator { while noNextValueAvailable { self.semaphore.wait() } return nextValue } } }

Slide 20

Slide 20 text

protocol IteratorProtocol { associatedtype Element public mutating func next() -> Self.Element? }

Slide 21

Slide 21 text

protocol Observer { associatedtype Element func next(_ element: Element) }

Slide 22

Slide 22 text

typealias Observer = (E) -> Void

Slide 23

Slide 23 text

Iterator : Sequence     

Slide 24

Slide 24 text

Iterator : Sequence  ::   Observer : Observable

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

let o = Observable(values: [1, 2, 3]) o.subscribe { print($0) } // prints: // 1 // 2 // 3

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

let o = Observable(values: [1, 2, 3]) o.subscribe { print($0) } o.append(4) // prints: // 1 // 2 // 3 // 4

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

let o = Observable() o.subscribe { print($0) } o.append("Foo") // prints: // Foo

Slide 33

Slide 33 text

let o = Observable() o.append("Bar") o.subscribe { print($0) } o.append("Foo") // prints: // Foo

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

let o = Observable { obs in obs(1) obs(2) obs(3) obs(4) } //

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

let o = Observable { obs in obs(1) obs(2) obs(3) obs(4) } o.subscribe { } // -------- let s = [1, 2, 3, 4] for elem in s { } s.forEach { }

Slide 39

Slide 39 text

let o = Observable { obs in obs(1) dispatchQueue.asyncAfter(deadline: .now() + .seconds(1)) { obs(2) } } o.subscribe { print($0) } // prints: // 1 // 2

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

func timer(delay: Int) -> Observable { 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!") }

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

let uikonf = response(url: URL(string: "http://www.uikonf.com")!) uikonf.subscribe { data, response in print(data) }

Slide 44

Slide 44 text

extension Observable { func same() -> Observable { return Observable { observer in self.subscribe(observer) } } }

Slide 45

Slide 45 text

extension Observable { func same() -> Observable { return Observable { observer in self.subscribe { elem in observer(elem) } } } }

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

let uikonf = response(url: URL(string: "http://www.uikonf.com")!) uikonf.subscribe { data, response in print(data) } uikonf.subscribe { data, response in print(data) }

Slide 51

Slide 51 text

Reactive programming Making duplicate networking requests has never been easier

Slide 52

Slide 52 text

Reactive programming Making duplicate networking requests has never been easier Sharing side-effects has never been easier

Slide 53

Slide 53 text

extension Observable { func share() -> Observable { return Observable { observer in // do something } } }

Slide 54

Slide 54 text

extension Observable { func share() -> Observable { return Observable { observer in } } }

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

extension Observable { func share() -> Observable { var subscribed = false var observers: [Observer] = [] return Observable { observer in observers.append(observer) if !subscribed { self.subscribe { e in for o in observers { o(e) } } subscribed = true } } } }

Slide 57

Slide 57 text

let uikonf = response(url: URL(string: "http://www.uikonf.com")!).share() uikonf.subscribe { data, response in print(data) } uikonf.subscribe { data, response in print(data) }

Slide 58

Slide 58 text

for e in seq { break }

Slide 59

Slide 59 text

let uikonf = response(url: URL(string: "http://www.uikonf.com")!).share() uikonf.subscribe { data, response in print(data) }

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

let uikonf = response(url: URL(string: "http://www.uikonf.com")!) let disposable = uikonf.subscribe { data, response in print(data) } // later... disposable()

Slide 65

Slide 65 text

class View { var disposable: Disposable? func bind(to viewModel: ViewModel) { disposable = viewModel.title().subscribe { [weak self] in self.titleLabel.text = $0 } } deinit { disposable?() } }

Slide 66

Slide 66 text

class ViewController { func submitButtonTapped() { Observable { obs in // ? } } }

Slide 67

Slide 67 text

class ViewController { let buttonTaps = Observable() func submitButtonTapped() { buttonTaps.send() } }

Slide 68

Slide 68 text

40 slides ago in a Kosmos not so faraway...

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

class Subject: Observable { let observers: Box<[Observer?]> init() { let observers = Box<[Observer?]>([]) 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) } } }

Slide 72

Slide 72 text

                                                             class ViewController { let buttonTaps = Subject()                        func submitButtonTapped() { buttonTaps.send() } }

Slide 73

Slide 73 text

                                                             class ViewController { let buttonTaps = Subject() func submitButtonTapped() { buttonTaps.send() } init() { let d = buttonTaps.subscribe { } } }

Slide 74

Slide 74 text

                                                             class ViewController { let buttonTaps = Subject() 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) } } } }

Slide 75

Slide 75 text

                                                             class ViewController { let buttonTaps = Subject() 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)> } } }

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

extension Observable where E == ObservableProtocol { func flatten() -> Observable { return Observable { 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() } } } } }

Slide 79

Slide 79 text

class ViewController { let buttonTaps = Subject() 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) } } }

Slide 80

Slide 80 text

✅ Observer ✅ Observable ✅ Operators ✅ Subscribing & Disposing ✅ Sharing side-effects ✅ Subject ✅ Combining Observables

Slide 81

Slide 81 text

→ .next(E), .error(Error) and .completed → Concurrency & Thread Safety → Operators, operators, operators

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Thanks!