Slide 1

Slide 1 text

Reactive State Machine 2016/08/20 #iOSDC Japan Yasuhiro Inami / @inamiy

Slide 2

Slide 2 text

ϦΞΫςΟϒ ϓϩάϥϛϯά

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

࣌ؒܦաʹ൐͏ ΠϕϯτετϦʔϜ

Slide 5

Slide 5 text

Swift FRP༻ϥΠϒϥϦ (FRP = Functional Reactive Programming) • RxSwift • ReactiveCocoa (RAC) • SwiftBond / ReactiveKit • Interstellar • ReactKit ؔ਺ܕϓϩάϥϛϯάͰɺએݴతʹɺσʔλϑϩʔΛߏங͢Δ

Slide 6

Slide 6 text

!Λͭ͘Δ !͕ྲྀΕΔ

Slide 7

Slide 7 text

!ΛͲ͏͢Δʁ ৯΂Δ εϧʔ

Slide 8

Slide 8 text

!ΛҭͯΔ

Slide 9

Slide 9 text

!ͷ؅ཧ

Slide 10

Slide 10 text

ঢ়ଶͷมߋʢData Bindingʣ final class ViewModel { var rawValue: ! let variable: RxSwift.Variable let mutableProperty: RAC.MutableProperty } mySuperEventEmitter.on { viewModel.rawValue = $0 } observable.bindTo(viewModel.variable) // RxSwift viewModel.mutableProperty <~ signal // ReactiveCocoa

Slide 11

Slide 11 text

ঢ়ଶͷऔಘʢData Observingʣ final class ViewModel { var rawValue: ! let variable: RxSwift.Variable let mutableProperty: RAC.MutableProperty } // `viewModel.rawValue`͸؂ࢹͰ͖ͳ͍ʂ (KVOͰͳ͍ݶΓ) viewModel.variable.asObservable().doOn {...} // RxSwift viewModel.mutableProperty.signal.on {...} // ReactiveCocoa

Slide 12

Slide 12 text

Observable or Signal ≒ EventEmitter

Slide 13

Slide 13 text

Variable or MutableProperty ≒ EventEmitter + ݱࡏͷ஋

Slide 14

Slide 14 text

FRP + MVVMͰ ঢ়ଶ؅ཧ͕ ͱͬͯ΋؆୯ʂ

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

ຊ౰ʹʁ

Slide 17

Slide 17 text

(Swift߃ྫ) ϙέϞϯʹྫ͑ͯΈΔ

Slide 18

Slide 18 text

final class ViewModel { let variable: Variable }

Slide 19

Slide 19 text

final class ViewModel { let variable: Variable } final class ViewModel2 { let variable: Variable }

Slide 20

Slide 20 text

final class ViewModel { let variable: Variable } final class ViewModel2 { let variable: Variable } final class ViewModel3 { let variable: Variable }

Slide 21

Slide 21 text

final class ViewModel { let variable: Variable } final class ViewModel2 { let variable: Variable } final class ViewModel3 { let variable: Variable } ... final class ViewModel1000 { let variable: Variable }

Slide 22

Slide 22 text

let variable = { n in viewModel[n].variable } variable(1).asObservable() .map { ... } .bindTo(variable(2)) variable(2).variable.asObservable() .flatMap { ... } .bindTo(variable(3)) Observable.combineLatest(variable(123), variable(456)) .map { ... } .bindTo(variable(789)) ...

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

1000ݸͷViewModel ͕৫Γͳ͢ ૖େͳεϖΫλΫϧ

Slide 27

Slide 27 text

఺ࡏͨ͠ແ਺ͷঢ়ଶ ܅͸খӉ஦Λײͨ͜͡ͱ͕͋Δ͔

Slide 28

Slide 28 text

FRP + MVVM ○ ϑϩʔߏங × ঢ়ଶ؅ཧ

Slide 29

Slide 29 text

FRP + MVVMͷ໰୊఺(1) // RxSwiftͰUI binding viewModel.variable.asDriver().drive(/* UIߋ৽ */) // 1. ೚ҙͷλΠϛϯάͰɺ͔ͭόϦσʔγϣϯͳ͠ʹɺ // ঢ়ଶมߋ͕Մೳ ! ʮՄมͳঢ়ଶʯ viewModel.variable.value = "!!! poops!" // ઒ΛԚ͢ਓ͕ݱΕΔ

Slide 30

Slide 30 text

final class ViewModel { var rawValue: T let variable: Variable } let variable͸ var rawValueͱ େͯ͠มΘΒͳ͔ͬͨ ΜͩΑʂʂʂ

Slide 31

Slide 31 text

FRP + MVVMͷ໰୊఺(2) // RxSwiftͰUI binding viewModel.variable.asDriver().drive(/* UIߋ৽ */) // 2. ೚ҙͷλΠϛϯάͰଞͷϑϩʔͱͭͳ͙ɾ֎͢͜ͱ͕Մೳ // ! ʮՄมͳσʔλϑϩʔʯ let disposable = !!!Observable .bindTo(viewModel.variable) // ͭͳ͙ disposable.dispose() // ֎͢

Slide 32

Slide 32 text

ʮσʔλϑϩʔʯ ͦͷ΋ͷ͕ ʮঢ়ଶʯ ͩͬͨΜͩ ʔʔʔʂʂʂ

Slide 33

Slide 33 text

FRP + MVVM ↓ ঢ়ଶʮૢ࡞ʯ͸ָʹͳͬͨ ↓ ͕ɺঢ়ଶͷ਺͸Ή͠Ζ૿͍͑ͯΔ

Slide 34

Slide 34 text

ຊ౰ʹঢ়ଶʮ؅ཧʯ ग़དྷͯΔʁ

Slide 35

Slide 35 text

!

Slide 36

Slide 36 text

໨·͙Δ͘͠มಈ͢Δ ʮσʔλϑϩʔʯ ༧ଌͰ͖ͳ͍ແ਺ͷ ʮঢ়ଶʯ

Slide 37

Slide 37 text

༧ଌՄೳͳʮঢ়ଶʯ Model-View-???

Slide 38

Slide 38 text

ൃ૝ͷస׵ σʔλϑϩʔͷݻఆ ঢ়ଶͷ਺Λ࠷খԽ

Slide 39

Slide 39 text

React.js

Slide 40

Slide 40 text

React.js • ViewΛ(ͳΔ΂͘)εςʔτϨεʹѻ͏ϑϨʔϜϫʔΫ • Component: ੜͷDOMͷ୅ΘΓʹϨϯμϦϯά • JSXΛ࢖ͬͯɺXMLϥΠΫʹπϦʔߏ଄Λهड़ • Componentͷঢ়ଶΛߋ৽͢Δͱɺࠩ෼ߋ৽ (Virtual DOM) • ਌͔Βࢠ΁ͷσʔλϑϩʔ • (ڱٛͷ)ϦΞΫςΟϒϓϩάϥϛϯάͱ͸ผ෺

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

React.jsͷ೉఺ ࢠ͔Β਌΁ͷ σʔλϑϩʔ͕ਏ͍

Slide 44

Slide 44 text

Redux

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

Redux • ΞϓϦͷશͯͷঢ়ଶΛɺ1ͭͷίϯςφ (Store) Ͱ؅ཧ • ΞϓϦ͸Ͱ͔͍ͬγϯάϧτϯ • ࠷্෦ͷ਌Component΋ঢ়ଶ؅ཧ͔Βղ์͞ΕΔ • Ͳͷ֊૚ͷComponent͔ΒͰ΋ɺίϯςφʹActionΛૹΔ͜ ͱ͕؆୯ • ࢠ͔Β਌΁ͷσʔλϑϩʔ

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

໰୊఺΋͋Δ

Slide 50

Slide 50 text

Reduxͷ໰୊఺ • MiddlewareͷҐஔ෇͚ • ReducerΛ௨Δલʹ෭࡞༻(IO)Λѻ ͏ • ReducerΛ௨Γͳ͕Β෭࡞༻Λ ॲཧ͍ͨ͠৔߹͸ʁ • ີ݁߹͕ඞཁͳέʔε͸ଟ͍ • ॱংґଘ

Slide 51

Slide 51 text

Reduxͷ໰୊఺ • ඇಉظॲཧ͕೉͍͠ • MiddlewareͷதʹThunk? Promise? ES6 Generator? Observable? • Q. ෳ਺ͷMiddleware͕ඇಉظͰ࿈࠯൓Ԡͨ͠ΒͲ͏ͳ Δʁ!"!"!"! • A. ਏ͍ɻඇಉظॲཧνΣʔϯͳΒɺObservableͷ࿈݁ʹ ೚ͤͨํ͕෼͔Γ΍͍͢

Slide 52

Slide 52 text

΋ͬͱྑ͍ઃܭΛ ߟ͑Δ!

Slide 53

Slide 53 text

ReduxͷܕΛோΊΔ • Action = ೖྗɺState = ঢ়ଶ • Store = ঢ়ଶίϯςφ • Reducer = (State, Action) -> State = ঢ়ଶભҠؔ਺ • Listener = State -> /* IO */ () = ঢ়ଶมߋΦϒβʔόʔ • Middleware = State -> ... -> Action -> /* IO */ Any = ෭࡞༻ϑοΫ

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

εςʔτϚγϯ • ܾఆੑʗඇܾఆੑɺ༗ݶʗແݶ • ΞΫηϓλʔʗϨίφΠβʔ • ϓογϡμ΢ϯɺνϡʔϦϯάɾϚγϯͳͲ • τϥϯεσϡʔαʔ • ϜʔΞɾϚγϯ (1956) • ϛʔϦɾϚγϯ (1955)

Slide 56

Slide 56 text

ϛʔϦɾϚγϯ (Σ, Ω, S, s0, δ, λ)

Slide 57

Slide 57 text

ϛʔϦɾϚγϯ ʮݱࡏͷঢ়ଶʯͱʮ֎෦ೖྗʯ͔Βɺʮ৽͍͠ঢ়ଶʯͱʮग़ ྗʯΛੜΈग़͢༗ݶτϥϯεσϡʔαʔ

Slide 58

Slide 58 text

ϛʔϦɾϚγϯ • Σ ͸ʮೖྗͷू߹ʯ • Ω ͸ʮग़ྗͷू߹ʯ • S ͸ʮঢ়ଶͷू߹ʯ • s0 ͸ʮॳظঢ়ଶʯʢs0 ∈ Sʣ • δ ͸ʮঢ়ଶભҠؔ਺ʯ δ: S x Σ → S • λ ͸ʮग़ྗؔ਺ʯ λ: S x Σ → Ω

Slide 59

Slide 59 text

Redux = ϛʔϦɾϚγϯ • Action = ೖྗ • State = ঢ়ଶ • Store = ॳظঢ়ଶʴঢ়ଶભҠؔ਺ • Reducer = ঢ়ଶભҠؔ਺ • Listener = ग़ྗ (IO) • Middleware = ग़ྗؔ਺

Slide 60

Slide 60 text

1955೥͔Β͋ͬͨ

Slide 61

Slide 61 text

͔࣌͠͠͸ɺ2016೥ ੈք͸FRPͷԌͰͭͭ·Εͨ

Slide 62

Slide 62 text

Redux + FRP

Slide 63

Slide 63 text

Redux + FRPʢվળҊʣ 1. ReducerͱMiddlewareΛ౷߹͢Δ: S x Σ -> S x Ω 2. ReducerͷฦΓ஋ΛOptionalʹ͢Δ • ঢ়ଶมԽ͕ͳ͍৔߹(ભҠࣦഊ)ɺෆཁͳdispatchΛආ͚Δ 3. FRP (ReactiveCocoa)Λ࢖͏ʢඇಉظνΣʔϯͷվળʣ 4. ग़ྗͷܕΛIO͔ΒɺʮIOʴ࣍ͷೖྗ΋ૹ৴Ͱ͖Δʯ ܕ SignalProducer Λ࢖͏

Slide 64

Slide 64 text

Reactive / Rx Automaton inamiy/ReactiveAutomaton inamiy/RxAutomaton

Slide 65

Slide 65 text

Redux ˰ ReactiveAutomaton • Store 㱺 Automaton • Action 㱺 Input • Reducer 㱺 (State, Input) -> (State, SignalProducer)? • Listener 㱺 Reply -> /* IO */ () • Middleware 㱺 !

Slide 66

Slide 66 text

࢖༻ྫ

Slide 67

Slide 67 text

αϯϓϧίʔυʢϩάΠϯॲཧʣ • State: LoggedOut, LoggingIn, LoggedIn, LoggingOut • Input: Login, LoginOK, Logout, LogoutOK, ForceLogout

Slide 68

Slide 68 text

αϯϓϧίʔυʢϩάΠϯॲཧʣ // 1. switch-caseΛ࢖ͬͨύλʔϯϚονϯά let mapping: NextMapping = { fromState, input in switch (fromState, input) { case (.LoggedOut, .Login): return (.LoggingIn, loginOKProducer) case (.LoggingIn, .LoginOK): return (.LoggedIn, .empty) case (.LoggedIn, .Logout): return (.LoggingOut, logoutOKProducer) case (.LoggingOut, .LogoutOK): return (.LoggedOut, .empty) case (.LoggingIn, .ForceLogout), (.LoggedIn, .ForceLogout): return (.LoggingOut, forceLogoutOKProducer) default: return nil } }

Slide 69

Slide 69 text

αϯϓϧίʔυʢϩάΠϯॲཧʣ let canForceLogout: State -> Bool = [.LoggingIn, .LoggedIn].contains // 2. ΧελϜԋࢉࢠΛ࢖ͬͨύλʔϯϚονϯά let mappings: [NextMapping] = [ /* input | fromState => toState | effect */ /* ---------------------------------------------------------- */ .Login | .LoggedOut => .LoggingIn | loginOKProducer, .LoginOK | .LoggingIn => .LoggedIn | .empty, .Logout | .LoggedIn => .LoggingOut | logoutOKProducer, .LogoutOK | .LoggingOut => .LoggedOut | .empty, .ForceLogout | canForceLogout => .LoggingOut | forceLogoutOKProducer ]

Slide 70

Slide 70 text

αϯϓϧίʔυʢϩάΠϯॲཧʣ let (inputSignal, observer) = Signal.pipe() let send = observer.sendNext let automaton = Automaton( state: .LoggedOut, input: inputSignal, mapping: reduce(mappings), // combining reducers strategy: .Latest ) automaton.replies.observeNext { print($0) }

Slide 71

Slide 71 text

αϯϓϧίʔυʢϩάΠϯॲཧʣ expect(automaton.state.value) == .LoggedIn // ϩάΠϯࡁΈ send(Input.Logout) expect(automaton.state.value) == .LoggingOut // ϩάΞ΢τதɾɾɾ // `logoutOKProducer`ʹΑΓɺࣗಈతʹϩάΞ΢τ׬ྃʹભҠ expect(automaton.state.value) == .LoggedOut // ϩάΞ΢τࡁΈ send(Input.Login) expect(automaton.state.value) == .LoggingIn // ϩάΠϯதɾɾɾ // ͦͷ··์͓͚ͬͯ͹ɺࣗಈతʹϩάΠϯ׬ྃʹͳΔ͕ɾɾɾ send(Input.ForceLogout) // ڧ੍ϩάΞ΢τൃಈ!"! expect(automaton.state.value) == .LoggingOut // ϩάΞ΢τதɾɾɾ // `forceLogoutOKProducer`ʹΑΓɺࣗಈతʹϩάΞ΢τ׬ྃʹભҠ

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

ReactiveAutomaton • খ͞ͳঢ়ଶ؅ཧ͔ΒͰ͔͍ͬγϯάϧτϯ·Ͱɺ෯޿͘ରԠ • Elmͷجຊઃܭʹ͍ۙ • Program + (ݴޠ಺) Effect Manager = ΦʔτϚτϯ • Model = ঢ়ଶɺMsg = ೖྗ • update : Msg -> Model -> (Model, Cmd Msg) = ঢ়ଶ ભҠʴIOग़ྗؔ਺

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

Model View ViewModel

Slide 76

Slide 76 text

Model View ✨Automaton✨

Slide 77

Slide 77 text

·ͱΊ • ʮσʔλϑϩʔʯͱ͍͏໊ͷʮঢ়ଶʯ • FRP + MVVMʹΑΔঢ়ଶ؅ཧ͸ґવͱͯ͠ෳࡶ • React.js + Redux͔ΒֶͿ • UIKitͷਐԽ΋ඞཁʢReactNativeͳͲʣ • Redux + FRP = Reactive State Machine • Elm͸ཁνΣοΫʢࢀߟɿ A Farewell to FRPʣ

Slide 78

Slide 78 text

Thanks!