$30 off During Our Annual Pro Sale. View Details »

SwiftUI時代のFunctional iOS Architecture / iOSDC J...

Yasuhiro Inami
September 21, 2020

SwiftUI時代のFunctional iOS Architecture / iOSDC Japan 2020

SwiftUI時代の Functional iOS Architecture by 稲見 泰宏 | トーク | iOSDC Japan 2020 - fortee.jp (Sep 21, 2020)
https://fortee.jp/iosdc-japan-2020/proposal/6d88c4dc-5a24-4c07-b505-65e2c389cdfb

Blog: https://zenn.dev/inamiy/articles/26b41ae9d81b83d5e9be
Book: https://zenn.dev/inamiy/books/795a2d9e6954abb46878

Yasuhiro Inami

September 21, 2020
Tweet

More Decks by Yasuhiro Inami

Other Decks in Programming

Transcript

  1. Reactive State Machine ͷৼΓฦΓ • MVVM ͔Β Redux / Elm

    Architecture (Mealy Machine) ΁ • Reducer = (Action, State) -> (State, Output) • Output = Publisher<Action, Never> • ΞϓϦͷσʔλϑϩʔΛ୯ํ޲ͷϧʔϓʹ୯७Խ͠ɺঢ়ଶ؅ ཧͱςετΛ͠΍͘͢͢Δ • ෭࡞༻ (IO) ʹؔ਺ܕϦΞΫςΟϒϓϩάϥϛϯά (FRP) Λ༻ ͍Δ
  2. Reactive State Machine ͷৼΓฦΓ • Proof of Concept • ReactiveAutomaton

    (ReactiveSwift൛) • RxAutomaton (RxSwift൛) • ͦͷଞ • React & Elm inspired frameworks in Swift • SwiftElmɿUIKit্ͷԾ૝ViewϑϨʔϜϫʔΫʢࢼݧ࣮૷ʣ
  3. ࠓ೔ͷΞδΣϯμ SwiftUI ࣌୅ͷ Functional iOS Architecture • inamiy/Harvest • pointfreeco/swift-composable-architecture

    • bow-swift/bow-arch • 3ͭͷϑϨʔϜϫʔΫͷڞ௨఺ͱҧ͍ʹ͍ͭͯ • ReactɺElm ͳͲͷWebϑϩϯτΤϯυͱͷൺֱ
  4. ! Harvest • Reactive State Machine (Elm Architecture෩) ࣮૷Λվྑ •

    SwiftUI + Combine ରԠ (HarvestStore) • Dependency Injection ίϯςφͱͯ͠΋ಇ͘ • FRPʹΑΔ ෭࡞༻ͷΩϡʔ؅ཧ ͱΩϟϯηϧॲཧ • Optics (Lens & Prism) Λ࢖ͬͨ State ͱ Action ͷ෦඼Խͱ Reducer ͷ߹੒
  5. ෭࡞༻ΛΩϡʔͰ؅ཧ͢Δ • Publisher (Observable): ࣌ؒґଘͷ෭࡞༻ͷετϦʔϜ • ετϦʔϜͷετϦʔϜ ( Publisher<Publisher<T>> )

    ͸ɺ ෭࡞༻ͷετϦʔϜΛʮΩϡʔ؅ཧʯ͍ͯ͠Δ͜ͱʹ૬౰ • Ωϡʔ؅ཧ͞Ε༷ͨʑͳετϦʔϜΛҰຊͷετϦʔϜʹ౷ ߹͢ΔʢʹϞφυ߹੒ɺϑϥοτԽʣ • Ωϡʔ؅ཧͷ ౷߹ઓུ (FlattenStrategy) • merge, concat, concurrent(max:), switchLatest, race ͳͲ
  6. Elm Architecture + FRP Λ૊Έ߹ΘͤΔར఺ • FRPͷຊ࣭͸ɺ Publisher ಺෦ͷঢ়ଶΛӅṭ͠ͳ͕Βσʔλ ϑϩʔΛߏஙͰ͖Δ

    ͜ͱ • ͨͩ͠ɺঢ়ଶΛӅṭ͢Ε͹͢Δ΄Ͳɺঢ়ଶ؅ཧ͕೉͘͠ͳΔ • Elm Architecture ʢσʔλϑϩʔͷ؆қԽͱঢ়ଶ؅ཧͷ௥ ٻʣΛϕʔεʹɺFRP Ͱʮͭ·Βͳ͍ঢ়ଶ؅ཧʯΛল͘ • ྫɿ throttle ࣌ͷલճೖྗ࣌ࠁͷه࿥ɺલड़ͷΩϡʔ؅ ཧ͞Εͨ෭࡞༻ͷ౷߹ܭࢉͳͲ
  7. Optics (Lens & Prism) • Lens: ঢ়ଶʢ௨ৗ͸ struct ௚ੵܕʣʹର͢Δ2ͭͷૢ࡞ •

    ྫɿϢʔβʔ໊ͷऔಘ (get) ͱߋ৽ (set) • Prism: ΞΫγϣϯ (௨ৗ͸ enum ௚࿨ܕ) ʹର͢Δ2ͭͷૢ࡞ • ྫɿ֤ case ͔ΒίϚϯυੜ੒ & Φϓγϣϯऔಘ (tryGet) struct User { enum Command { var name: String case rm(rf: Bool) } }
  8. struct Lens<Whole, Part> { /// struct ϝϯόม਺ͷऔಘ let get: (Whole)

    -> Part /// struct ϝϯόม਺ͷߋ৽ let set: (Whole, Part) -> Whole } struct Prism<Whole, Part> { /// enum case ͷ associated values ͷऔಘ let tryGet: (Whole) -> Part? /// case ؔ਺ʢenum ίϯετϥΫλʣ let build: (Part) -> Whole }
  9. /// Reducer ͷ ChildState Λ ਌ State ʹม׵ʢ൓มؔखʣ func contramapS<Action,

    State, ChildState> (_ lens: Lens<State, ChildState>) // ਌͔Βࢠ΁ͷࣹ -> Reducer<Action, ChildState> // ࢠ͔Β -> Reducer<Action, State> // ਌΁ͷࣹ /// Reducer ͷ ChildAction Λ ਌ Action ʹม׵ʢ൓มؔखʣ func contramapA<Action, ChildAction, State> (_ prism: Prism<Action, ChildAction>) // ਌͔Βࢠ΁ͷࣹ -> Reducer<ChildAction, State> // ࢠ͔Β -> Reducer<Action, State> // ਌΁ͷࣹ
  10. Reducer<Action, State> ͷܕม׵ͱ߹੒ func combine<A, S>(reducers: [Reducer<A, S>]) { ...

    } let childReducer1: Reducer<ChildAction1, ChildState1> = ... let childReducer2: Reducer<ChildAction2, ChildState2> = ... // ࢠίϯϙʔωϯτ1,2ͷ Reducer Λ਌Λج४ʹม׵ͯ͠߹੒͢Δ let reducer: Reducer<Action, State> = combine([ contramapS(lens1)(contramapA(prism1)(childReducer1)) contramapS(lens2)(contramapA(prism2)(childReducer2)) ])
  11. Optics ·ͱΊ • ΞϓϦͷ State (ओʹ struct) ͱ Action (ओʹ

    enum) Λڊେʹ ͤͣɺπϦʔߏ଄ʹ෼ղͯ͠ɺݸʑͷίϯϙʔωϯτ͝ͱʹ ؅ཧ͢Δʢૄ݁߹Խʣ • ݸʑͷίϯϙʔωϯτͷ Reducer Λશମʹ·ͱΊΔ ࡍʹ Lens ͱ Prism ͕׆༂͢Δ (react-reduxͷ্Ґ൛) • Optics ͸ ؔ਺ܕϓϩάϥϛϯάͱݍ࿦ Λ࢖ͬͨ୅දతͳς ΫχοΫͷ1ͭ
  12. Composable Architecture (TCA) • Point-Free νʔϜʹΑΔ Elm Architecture ෩ͷ࣮૷ •

    Multi-Storeํࣜɿ ࠷্ҐͰ͸ͳ͘ɺ਌ʢ্ྲྀʣͱࢠʢԼ ྲྀʣίϯϙʔωϯτ֤ʑʹରͯ͠ϦΞΫςΟϒʹΠϕϯτ Λ఻ൖ͢Δ ʴ ॏෳ๷ࢭॲཧ • swift-case-paths Λ࢖ͬͨ Prism ࣮૷ • Lens ͸Swiftඪ४ͷ WritableKeyPath Λ࢖ͬͯ୅༻ • ˑ2000Ҏ্ɺυΩϡϝϯτͱϏσΦνϡʔτϦΞϧ͕ॆ࣮
  13. Case Paths struct User { var name: String } //

    WritableKeyPath<User, String> let keyPath = \User.name // όοΫεϥογϡ enum Command { case rm(rf: Bool) } // CasePath<Command, Bool> let casePath = /Command.rm // ௨ৗͷεϥογϡ
  14. struct CasePath<Root, Value> { // `Prism` ͱಉܕ let embed: (Value)

    -> Root let extract: (Root) -> Value? } prefix func / <Root, Value>( embed: @escaping (Value) -> Root ) -> CasePath<Root, Value> { /* ! Magic inside */ } " prefix func / " ͷ಺෦࣮૷ Swift ͷϦϑϨΫγϣϯΛ࢖ͬͯɺembedʢcaseؔ਺ʣ͔Β extract Λࣗಈతʹಋग़͢Δ (ར఺ɿcodegen͕ཁΒͳ͍)
  15. Bow Arch • Tomás Ruiz-López ࢯ (47 Degrees ࣾ) ࡞ͷ

    UI Architecture • Bow: Lightweight Higher Kinded Polymorphism (ߴΧΠϯυ ଟ૬) Λ࢖ͬͨؔ਺ܕϓϩάϥϛϯά༻ϥΠϒϥϦ • func foo<M: Monad> ͷΑ͏ͳॻ͖ํ͕Ͱ͖Δ • ࢀߟɿSwiftͰߴΧΠϯυଟ૬ - Speaker Deck • Comonadic UI: ݍ࿦ʢ਺ֶʣΛ࢖ͬͨUIઃܭख๏
  16. ίϞφυ ≈ ΦϒδΣΫτࢦ޲ − Մมࢀর • SwiftUI.View Λ ίϞφυ ͱͯ͠ߟ͑Δ

    • ίϞφυ: (಺෦)ঢ়ଶΛ࣋ͪɺঢ়ଶ͔Βܭࢉͨ݁͠ՌΛฦ͢ • ྫɿΠςϨʔλ͸ɺ಺෦ঢ়ଶΛߋ৽͠ͳ͕Β next Λग़ྗ • ྫɿϏϧμʔ͸ɺड͚औͬͨύϥϝʔλΛ಺෦ঢ়ଶʹอ࣋ ͯ͠ɺ࠷ऴ݁ՌΛग़ྗ • ྫɿReact Component (SwiftUI) ͸ɺঢ়ଶΛ࣋ͪͭͭɺ render (body) ͰԾ૝DOM (View) Λग़ྗ
  17. struct Component<S, V: View>: View { // ݱࡏͷঢ়ଶ var state:

    S // ݱࡏͷঢ়ଶ͔ΒԾ૝ViewΛܭࢉ let _body: (S) -> V // Note: static var Ͱߟ͑Δͱɺܕ͸ `Self -> V` var body: V { _body(state) } } Component<S, V> = W<V> ͱ͓͘ͱɺ body: W<V> -> V ɾɾɾ ίϞφυʹॏཁͳ extract ͷੑ࣭
  18. // Ϟφυ M = UFʢίϯςΩετΛੜ੒͢Δܭࢉʣɹɹɹ// NOTEɿٙࣅSwift protocol Monad[M] where Functor[M]

    { static func `return`<C>(_ c: C) -> M<C> // η = unit static func join<C>(_ mmc: M<M<C>>) -> M<C> // static func flatMap<C, C2>(_ f: C -> M<C2>) // -> M<C> -> M<C2> } // ίϞφυ W = FUʢίϯςΩετΛফඅ͢Δܭࢉʣ protocol Comonad[W] where Functor[W] { static func extract<D>(_ wd: W<D>) -> D // ε = counit static func duplicate<D>(_ wd: W<D>) -> W<W<D>> // static func extend<D, D2>(_ f: W<D> -> D2) // -> W<D> -> W<D2> }
  19. ίϞφυ (Comonad) // ٙࣅSwift protocol Comonad[W] where Functor[W] { static

    func extract<D>(_ wd: W<D>) -> D static func duplicate<D>(_ wd: W<D>) -> W<W<D>> } • extract: ΦϒδΣΫτ W<D> ͔ΒίϯςΩετΛফඅͯ͠ D Λग़ྗʢe.g. ঢ়ଶΛ࢖ͬͯ View Λग़ྗʣ • duplicate: ΦϒδΣΫτͷऔΓ͏ΔະདྷਤΛੜ੒ʢޙड़ʣ
  20. duplicate ͷΠϝʔδʢྫɿແݶετϦʔϜʣ let stream = [ 0, 1, 2, ...

    ] // ݱ࣌఺ͷແݶετϦʔϜ // ʢݱࡏͱະདྷͷʣແݶετϦʔϜΛཁૉʹ࣋ͭແݶετϦʔϜ duplicate(stream) = [ [ 0, 1, 2, ... ], // ݱࡏͷ `stream` Λෳ੡ [ 1, 2, 3, ... ], // ෳ੡ + shift [ 2, 3, 4, ... ], // ෳ੡ + shift 2ճ [ 3, 4, 5, ... ], // ෳ੡ + shift 3ճ ... ]
  21. duplicate ͷΠϝʔδʢྫɿSwiftUI / Reactʣ let makeComp: (S) -> Component =

    Component(_body: ...) // ෦෼ద༻ let component: Component = makeComp(state: ...) // ݱࡏͷ Component // ʢݱࡏͱະདྷͷʣComponent Λੜ੒͢Δ Component duplicate(component) = Component(_body: makeComp, state: component.state) ≈ [ /* ݱࡏͷ Component ͷෳ੡ */, /* ෳ੡ + ঢ়ଶͷҰ෦͕มߋ͞Εͨ Component */, /* ෳ੡ + ঢ়ଶͷผͷҰ෦͕มߋ͞Εͨ Component */, ... ] // Component ͕औΓ͏Δશͯͷঢ়ଶͷۭؒ
  22. Component ≅ Store ίϞφυ struct Component<S, V: View> { //

    ݱࡏͷঢ়ଶ var state: S // ݱࡏͷঢ়ଶ͔ΒԾ૝ViewΛܭࢉ let _body: (S) -> V } struct Store<S, A> { let state: S let render: (S) -> A }
  23. ࣮ࡍͷ ComponentʢΠϕϯτϋϯυϥ෇͖ʣ struct Component<S, V: View> { // ݱࡏͷঢ়ଶ var

    state: S // ݱࡏͷঢ়ଶͱʮΠϕϯτϋϯυϥʯΛ౉ͯ͠Ծ૝ViewΛܭࢉ let view: (S) -> (EventHandler) -> V } typealias EventHandler = (Action) -> IO<Void> /* ෭࡞༻ */
  24. ͜͜Ͱɺ typealias UI<V> = (EventHandler) -> V ͱ͓͘ͱɺ struct Component<S,

    V: View> { var state: S let view: (S) -> UI<V> } Component<S, V> ͸ Store<S, UI<V>> ͱ͍͏ܗͰද͞ΕΔ
  25. ίϞφυͷ಺෦ঢ়ଶΛ֎͔Βૢ࡞͢Δ ઌ΄ͲͷແݶετϦʔϜͷྫͰ͸ɺ shift ʢ0, 1, 2, l߭Ďůሑ Ⅲ⴬۷ƑҠቔƎཌྷ֒čǸǠǟƵ൐ƆƂDžƾȀDSLͰදͤΔʣ // ແݶετϦʔϜʢίϞφυʣ

    // Shiftૢ࡞ʢϞφυʣ indirect enum Stream<A> { indirect enum Shift<A> { case cons(A, Stream<A>) case done(A) } case shift(Shift<A>) } Q. ͜ΕΒ2ͭͷܕ͸ͲͷΑ͏ͳؔ܎ʹ͋Δͷ͔ʁ
  26. A. Stream ίϞφυͱ Shift Ϟφυ͸ʮϖΞ (Pairing)ʯΛͳ͢ protocol Pairing[F, G] {

    // ݍ࿦Ͱݴ͏ɺDayʢΞϓϦΧςΟϒʣ߹੒ੵ͔Β߃౳ؔख΁ͷࣗવม׵ static func pair<A, B, C>(f: (A, B) -> C) -> F<A> -> G<B> -> C } extension Pairing[Shift, Stream] { static func pair<A, B, C>(f: (A, B) -> C) -> Shift<A> -> Stream<B> -> C { switch (shift, stream) { case let (.done(a), .cons(b, _)): return f(a, b) case let (.shift(nextShift), .cons(_, nextStream)): return pair(f)(nextShift)(nextStream) } } }
  27. Pairing Λ࢖͏ͱɺϞφυͰද͞ΕΔঢ়ଶૢ࡞ʹΑͬͯɺίϞ φυͷະདྷΛܾΊΔ͜ͱ͕Ͱ͖Δɻ // `stream` ͷະདྷΛ `shift` ͰܾΊΔ func select<B>(shift:

    Shift<()>, stream: Stream<B>) -> Stream<B> { pair({ _, stream in stream })(shift)(duplicate(stream)) } // Ұൠతʹɺ೚ҙͷϞφυɾίϞφυͷϖΞʹ͍ͭͯ੒Γཱͭ func select<M, W, B>(monad: M<()>, comonad: W<B>) -> W<B> where Monad[M], Comonad[W], Pairing[M, W] { pair({ _, comonad in comonad })(monad)(duplicate(comonad)) }
  28. ͳ͓ɺ SwiftUI ্ͷ ComponentʢStore ίϞφυʣͷ৔߹ɺঢ় ଶૢ࡞ͱͯ͠ State ϞφυΛ࢖͏͜ͱ͕Ͱ͖Δʢཧ༝͸ޙड़ʣ struct Store<S,

    A> { let state: S let render: (S) -> A } struct State<S, A> { let runState: S -> (A, S) } extension Pairing[State, Store] { ... }
  29. Stream ίϞφυ → Shift Ϟφυ Store ίϞφυ → State Ϟφυ

    Q. ίϞφυͷϖΞͱͳΔϞφυ͸ ͲͷΑ͏ʹݟ͚ͭΔͷ͔ʁ
  30. // ݍ࿦Ͱݴ͏ɺComonadʹԊͬͨ߃౳ؔखͷӈKan Liftɻ // ೚ҙͷίϞφυ `W` ʹରԠ͢Δʢঢ়ଶૢ࡞ͷʣϞφυ `Co<W>` Λߏ੒͢Δ͜ͱ͕Մೳɻ struct

    Co<W, A> { let runCo<R>: W<A -> R> -> R } extension Monad[Co<W>] where Comonad[W] { static func `return`<C>(_ c: C) -> Co<W, C> { Co { wf in W.extract(wf)(c) } } static func join<C>(_ mmc: Co<W, Co<W, C>>) -> Co<W, C> Co { (wc2r: W<C -> R>) in mmc.runCo( W.extend({ wc2r in { (mc: Co<W, C>) in mc.runCo(wc2r) } })(wc2r) ) } } }
  31. Co<W, A> Λ࢖͏ͱɺྫ͑͹ Co<Store<S>, A> ≅ State<S, A> ΛಘΔɻ ূ໌ɿ

    Co<W, A> ≅ ∀R. W<A -> R> -> R ΑΓ Co<Store<S>, A> ≅ ∀R. Store<S, A -> R> -> R ≅ ∀R. (S, (S -> A -> R)) -> R ≅ S -> ∀R. ((S, A) -> R) -> R // ΧϦʔԽ ≅ S -> (S, A) // ถాͷิ୊ ≅ State<S, A>
  32. ͜͜·Ͱͷ੔ཧ (Comonadic UI) • ίϞφυͷ extract ʹΑͬͯɺঢ়ଶ͔ΒViewΛੜ੒͢Δ • ίϞφυͷ duplicate

    ʹΑͬͯɺUIͷະདྷਤΛ࡞Δ • ίϞφυͷϖΞͱͳΔ ঢ়ଶૢ࡞༻ͷϞφυΛ Co<W, A> Ͱࢦ ఆ ͢Δ • select ͰίϞφυͱঢ়ଶૢ࡞༻ϞφυͷϖΞ͔Βɺ ະདྷͷ UI༻ίϞφυΛબ୒͢Δ
  33. ίϞφυ × ෭࡞༻ (IO) // ίϞφυΛϥοϓͯ͠ɺະདྷબ୒ͱ෭࡞༻ͷ࣮ߦʢՄมࢀরͷߋ৽ΛؚΉʣΛߦ͏ class EffectComponent<W, V>: ObservableObject

    where Comonad[W] { @Published var comonad: W<UI<V>> func explore() -> V { W.extract(comonad) { (action: IO<Co<W, Void>>) in action.flatMap { (monad: Co<W, Void>) in let nextComonad = select(monad, self.comonad.duplicate()) return IO<Void>.invoke { self.comonad = nextComonad } } } } }
  34. ίϞφυ × ෭࡞༻ = ΦϒδΣΫτࢦ޲ϓϩάϥϛϯά (OOP) • EffectComponent<W, V> ͸

    OOPʹ͓͚ΔʮΦϒδΣΫτʯ ʢͦͷෳࡶ͞ΛʮίϞφυʯͱʮ෭࡞༻ʯʹ෼ղ͢Δʣ • W = Store ίϞφυͷ৔߹ɺ ϖΞͱͳΔ ঢ়ଶϞφυͱͦΕʹ ൐͏෭࡞༻͸ɺ setState ʢঢ়ଶͷʮ௚઀ߋ৽ʯʣͱ౳Ձ • React ΍ SwiftUIʢ@State ͷՄมࢀরʣͷੈք؍ͱҰக • Note: ίϯϙʔωϯτͷωετ͸ɺίϞφυม׵ࢠΛ࢖͏
  35. Moore ίϞφυ indirect enum Moore<I, A> { // I =

    ೖྗɺ A = ग़ྗ // ݱࡏͷग़ྗͱɺʮೖྗ͔ΒະདྷͷMooreίϞφυʯͷੜ੒ؔ਺ case runMoore(A, I -> Moore<I, A>) } ∃S. (initial: S, reducer: S -> I -> S, render: S -> A) ͱಉܕ (Moore State Machineɺঢ়ଶͷʮؒ઀ߋ৽ʯ) ࣮૷ྫɿ Elm Architecture, Redux
  36. ༨ࣗ༝ίϞφυ (Cofree) // ೚ҙͷؔख `F` ʹରͯ͠ɺίϞφυΛ࡞Δ͜ͱ͕Ͱ͖Δɻ // `F(X) = I

    -> X` ͷͱ͖ɺMooreɻ`F(X) = ()` ͷͱ͖ɺ㱽S.Store<S>ɻ indirect enum Cofree<F, A> { case runCofree(A, F<Cofree<F, A>>) } ϖΞͱͳΔࣗ༝Ϟφυ (Free<F, A>) ͕ΫΤϦDSLʹͳΔ ࣮૷ྫɿ PureScript Halogen
  37. ·ͱΊ • SwiftUI ͷຊ࣭͸ ʮίϞφυʯ • ίϞφυͷߏ଄͕ UIΞʔΩςΫνϟʔͷύλʔϯ ΛܾΊΔ •

    React, Elm, PureScript Halogen, etc... • ঢ়ଶɺΞΫγϣϯɺ Reducer Λ֤ίϯϙʔωϯτ͝ͱʹ෼ղ ͠ɺ Optics Λ࢖ͬͯ·ͱΊ্͛Δ • ؔ਺ܕϓϩάϥϛϯάͱݍ࿦ɿ ຊ࣭Λཧղ͢Δڧྗͳख๏
  38. Harvest TCA Bow Arch GitHub Stars ˑ 300 ˑ 2000

    ˑ 100 ࡞ऀͷ΍Δؾ ❓ ೉қ౓ Medium Medium Hard ෭࡞༻ Combine Combine BowEffects Optics FunOptics WritableKeyPath & CasePaths BowOptics Comonadic UI Moore Moore Any Comonads ӨڹΛड͚ͨݴޠ Elm Elm & React PureScript & ݍ࿦
  39. References (Libraries & Optics) • ! Harvest: Apple's Combine.framework +

    State Machine, inspired by Elm • Composable Architecture • bow-arch: Comonadic UIs • Brandon Williams - Lenses in Swift • Lenses and Prisms in Swift: a pragmatic approach | Fun iOS
  40. References (Comonadic UI) • Declarative UIs are the Future —

    And the Future is Comonadic! • The Future Is Comonadic! - Speaker Deck • Comonads for user interfaces - Arthur Xavier • A Real-World Application with a Comonadic User Interface • The Comonad.Reader » Monads from Comonads
  41. References (෬ઢճऩ) • Reactive State Machine (Japanese) - Speaker Deck

    • SwiftͰElmΛ࡞Δ (Japanese) - Speaker Deck • SwiftͰߴΧΠϯυଟ૬ - Speaker Deck • ݍ࿦ͱSwift΁ͷԠ༻ / iOSDC Japan 2018 - Speaker Deck • ϓϩάϥϚͷͨΊͷϞφυ(ݍ࿦) - Speaker Deck • ݍ࿦ͱϓϩάϥϛϯά - Speaker Deck