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

VueFlux - Flux inspired state managements

VueFlux - Flux inspired state managements

2018/03/22 iOS Flux/Redux勉強会
Twitter hashtag: #iOSFluxRedux勉強会

VueFlux: https://github.com/ra1028/VueFlux
VueFluxExample-GitHub: https://github.com/ra1028/VueFluxExample-GitHub

439ebe4787a98881df8a59d41baf4a43?s=128

Ryo Aoyama

March 22, 2018
Tweet

Transcript

  1. VueFlux Flux inspired state managements 2018/03/22 iOS Flux / Redux

    ษڧձ #iOSFluxReduxษڧձ Ryo Aoyama - @ra1028
  2. Profile - Ryo Aoyama - CyberAgent, Inc / FRESH! /

    2016/04~ - GitHub: @ra1028 - Twitter: @ra1028fe5
  3. None
  4. όϥΤςΟʹ෋ΜͩϥΠϒಈըͷ഑৴ϓϥοτϑΥʔϜ ϩάΠϯෆཁɾߴը࣭ɾ௿஗ԆͰݟ์୊

  5. State managements ঢ়ଶ؅ཧ

  6. MVVM

  7. ෳࡶԽͷҰ్Λḷͬͨଟ༷ͳViewͷঢ়ଶΛਖ਼͘͠؅ཧ͢Δ ͨΊը໘Λߏ੒͢ΔϨΠϠΛModel-View-ViewModelʹ෼ׂ MVVMͷΞϓϩʔν

  8. MVVMͷΞϓϩʔν ViewͷUser interactionʹΑΔΠϕϯτΛViewModelʹૹ৴ ViewModel͸Model͔ΒσʔλΛऔಘ͠ɺ ߋ৽ΛData-binding(ଟ͘͸FRP)ʹΑͬͯViewʹ௨஌͢Δ

  9. "ঢ়ଶ؅ཧ" ؆୯͡Όͳ͍͔ʂʂ

  10. ຊ౰ͩΖ͏͔ ΋͏Ұาߟ͑ΛਐΊͯΈΔ

  11. MVVMͷ໰୊఺ - ੹຿աଟ ঢ়ଶͷߋ৽ɺσʔλͷऔಘɺViewʹඞཁͳ஋΁Ճ޻ɺetc... UIҎ֎ͷϩδοΫΛຆͲΛ੥͚ෛ͏ͨΊɺංେԽ͠΍͍͢

  12. MVVMͷ໰୊఺ - ঢ়ଶߋ৽ͷݟ௨͠ͷѱ͞ ViewModel͸಺෦ͰಉظɾඇಉظॲཧΛߦ͏ͨΊɺ ࣮૷͕ෳࡶʹͳΔʹ൐͍ݟ௨͕͠ѱ͘ͳΔ εϨου΁ͷؾݣ͍΋ඞཁ

  13. MVVMͷ໰୊఺ - ૒ํ޲σʔλϑϩʔʹΑΔෳࡶੑ FRPΛར༻ͨ͠૒ํ޲ͷσʔλϑϩʔ͸ɺ ඇৗʹ௥੻͠ʹ͍͘

  14. MVVMͷ໰୊఺ - ViewҎ֎ͷঢ়ଶʹؔ͢ΔϨΠϠ͕ͳ͍ Viewͷঢ়ଶ͸ViewModelʹ෼཭Ͱ͖͕ͨɺྫ͑͹ΞϓϦશମͰ ڞ༗͢Δঢ়ଶʹؔ༩͢Δ࢓૊Έ͸ͳ͍ γϯάϧτϯͷManagerΫϥεͷग़ݱ...

  15. ৗʹ໖ີʹઃܭ͞ΕͨMVVM͸໰୊ͳ͍ʂ ൓໘ 
 ঢ়ଶ؅ཧͱݴ͏ʹ͸ ͋·Γʹශऑͳ࢓૊Έ͔͠ͳ͔ͬͨ...

  16. ߋʹద੾ͳঢ়ଶ؅ཧϑϩʔͱϨΠϠ෼ׂ͕๬·ΕΔ

  17. ୯Ұํ޲σʔλϑϩʔΛݫີͳڧ੍ ঢ়ଶมߋͷϩδοΫΛཧղɾ༧ଌՄೳʹ؅ཧ͢Δ Unidirectional Data Flow

  18. Flux

  19. ঢ়ଶΛ؅ཧ͢ΔϨΠϠΛStore-Dispatcher-Action-Viewʹ ෼ׂ͢Δ͜ͱͰ୯Ұํ޲σʔλϑϩʔΛ࣮ݱ͢Δ FluxͷΞϓϩʔν

  20. ঢ়ଶΛมߋ͢Δࡍ͸ɺඞͣActionΛൃߦ͠Dispatcherʹૹ৴͢Δ Store͸DispatcherͷίʔϧόοΫͰͷΈࣗ਎ͷঢ়ଶΛߋ৽͢Δ View͸Storeͷঢ়ଶΛ؂ࢹ͠ɺදࣔΛߋ৽ͤ͞Δ FluxͷΞϓϩʔν

  21. ͜Ε͸͍͍΋ͷͩ

  22. ͔͠͠ݒ೦΋͋Δ

  23. Fluxͷݒ೦ - Storeͷ࣮૷͕։ൃऀʹҕͶΒΕ͍ͯΔ ෼ׂ୯Ґʹܾ·Γ͕ͳ͍ Dispatcherͷߪಡ։࢝ɾղআͷ࣮૷ɺঢ়ଶͷߋ৽ɺView༻ͷ஋ ΁ͷม׵ͳͲ࣮૷͢΂͖੹຿͸ґવͱͯ͠ଟ͍

  24. Fluxͷݒ೦ - Dispatcher͕γϯάϧτϯ FluxͰ͸Dispatcher͕ΞϓϦʹ།ҰͳͷͰɺStoreݸผʹdispatch͕Ͱ͖ͳ͍ શͯͷActionͷܕΛڐ༰͢ΔͷͰܕ৘ใ͕εϙΠϧ͞Εɺ໢ཏੑ΋ͳ͘ͳΔ

  25. ͦ͏͍͑͹Redux͸Ͳ͏ͳͷʁ

  26. Reduxͷݒ೦ Store͕γϯάϧτϯͰState͕πϦʔঢ়ʹूத؅ཧ͞Ε͍ͯΔ iOSͷ৔߹͸ෳ਺ͷಉ͡ը໘͕ಉ࣌ʹଘࡏ͢Δ(ChildViewController΋ؚΉ)έʔε ͕ଟ͍ͨΊɺݸผͷߋ৽͕೉͍͠

  27. Reduxͷݒ೦ ReduxͰѻ͏State͸immutableͰ͋ΓɺભҠؔ਺΋෭࡞༻ͷͳ͍७ਮؔ਺ ར఺ͱҾ͖׵͑ʹɺશͯͷStateͷͲΕ͕ߋ৽͞Εͯ΋௨஌͞ΕΔ WebͰ͸VDOMΛ࢖ͬͨࠩ෼ߋ৽ʹΑͬͯޮ཰తͳ൓ө͕Ͱ͖Δ͕ɺiOSͷ৔߹͸ UIKitͷ࠶࣮૷͕ඞཁʹͳΔ...

  28. None
  29. iOSͷจԽʹ࠷దԽͨ͠FluxΛߟ͑Δ

  30. VueFlux / VueFluxReactive https://github.com/ra1028/VueFlux Unidirectional State Management Architecture for Swift

  31. ঢ়ଶΛ؅ཧ͢ΔϨΠϠΛStore-Actions-Mutations-State-Computed-View ʹ෼ׂ͢Δ͜ͱͰ୯Ұํ޲σʔλϑϩʔΛ࣮ݱ͢Δ VueFluxͷΞϓϩʔν

  32. View͸஋ͷߋ৽ΛData bindingͰ൓ө͢Δ ReactiveSwift, RxSwiftΛಋೖΛඞਢʹ͠ͳ͍Α͏ʹ؆қతͳ Reactive SystemͰ͋ΔVueFluxReactiveΛ࢖͏ͷ΋Մೳ VueFluxͷΞϓϩʔν

  33. σʔλͷऔಘͳͲΛߦ͍ɺͦͷ݁ՌΛActionͱͯ͠dispatch͢Δ Action͸಺෦తʹDispatcherΛհͯ͠Mutationsʹcommit͞ΕΔ Actions

  34. Actions enum UserAction { case searched(result: Result<[User], Session.Error>) } extension

    Actions where State == UserState { func search(query: String) -> Disposable { let request = SearchUser(query: query, page: 1, perPage: 100) return Session.send(request: request).observe { result in self.dispatch(action: .searched(result: result)) } } }
  35. Actions enum UserAction { case searched(result: Result<[User], Session.Error>) } extension

    Actions where State == UserState { func search(query: String) -> Disposable { let request = SearchUser(query: query, page: 1, perPage: 100) return Session.send(request: request).observe { result in self.dispatch(action: .searched(result: result)) } } } ঢ়ଶΛભҠ͢ΔͨΊͷActionΛఆٛ͢Δ ܕ͸GenericsͰܾఆ͞ΕΔͷͰͳΜͰ΋OK
  36. enum UserAction { case searched(result: Result<[User], Session.Error>) } extension Actions

    where State == UserState { func search(query: String) -> Disposable { let request = SearchUser(query: query, page: 1, perPage: 100) return Session.send(request: request).observe { result in self.dispatch(action: .searched(result: result)) } } } Actions APIϦΫΤετͷ݁Ռ౳ʹԠͯ͡ActionΛૹ৴ ݕࡧ݁ՌΛdispatch
  37. ൃߦ͞ΕͨActionΛγʔέϯγϟϧʹҰఆͷContext(εϨου౳)ͰMutaionsʹcommit͢Δ VueFlux಺෦ͰӅṭ͞Ε͍ͯΔͷͰ࣮૷ऀ͸ҙࣝ͠ͳͯ͘ྑ͍ Dispatcher

  38. ౉͞ΕͨStateͱActionʹԠͯ͡ࢀরܕͰ͋ΔStateʹ௚઀มߋΛՃ͑Δ VueFluxͰStateͷมߋΛ།Ұڐ͞ΕΔϨΠϠ Mutations

  39. struct UserMutations: Mutations { func commit(action: UserAction, state: UserState) {

    switch action { case .searched(result: .success(let users)): state.cellModels.value = users.map(UserCellModel.init) case .searched(result: .failure): state.cellModels.value.removeAll() } } } Mutations
  40. struct UserMutations: Mutations { func commit(action: UserAction, state: UserState) {

    switch action { case .searched(result: .success(let users)): state.cellModels.value = users.map(UserCellModel.init) case .searched(result: .failure): state.cellModels.value.removeAll() } } } Mutations ActionʹPayloadͱͯ͠ఴ෇͞Εͨ஋Λ࢖ͬͯStateΛߋ৽͢Δ
  41. StoreʹΑͬͯ؅ཧ͞ΕΔ࣮ࡍͷঢ়ଶ ࣗ਎ͷϥΠϑλΠϜʹ͍ͭͯ஌Δඞཁ͸ͳ͍ State

  42. State final class UserState: State { typealias Action = UserAction

    typealias Mutations = UserMutations fileprivate let cellModels = Variable<[UserCellModel]>([]) }
  43. State final class UserState: State { typealias Action = UserAction

    typealias Mutations = UserMutations fileprivate let cellModels = Variable<[UserCellModel]>([]) } State͕ActionͱMutationsΛassociatedtypeʹ࣋ͪɺܕΛଋറ͢Δ
  44. State final class UserState: State { typealias Action = UserAction

    typealias Mutations = UserMutations fileprivate let cellModels = Variable<[UserCellModel]>([]) } MutationsΛಉ͡ϑΝΠϧ಺ʹهड़͠ɺfileprivateʹ͢Δ͜ͱͰ MutationsҎ֎͔ΒͷมߋΛڐՄ͠ͳ͍
  45. Stateͷmutableͳ஋ΛImmutableͳView༻ͷ஋ʹม׵ͯ͠ެ։͢Δ Computed

  46. Computed extension Computed where State == UserState { var cellModels:

    Constant<[UserCellModel]> { return state.cellModels.constant } var countText: Signal<String> { return state.cellModels.signal.map { String($0.count) } } }
  47. extension Computed where State == UserState { var cellModels: Constant<[UserCellModel]>

    { return state.cellModels.constant } var countText: Signal<String> { return state.cellModels.signal.map { String($0.count) } } } Computed ஋Λ௚઀ߋ৽Ͱ͖ͳ͍Α͏ʹɺimmutableʹม׵͢Δ
  48. extension Computed where State == UserState { var cellModels: Constant<[UserCellModel]>

    { return state.cellModels.constant } var countText: Signal<String> { return state.cellModels.signal.map { String($0.count) } } } Computed View͕࢖͍΍͍͢஋ʹม׵͢Δ
  49. Dispatcher͔ΒActionΛड͚औͬͯMutationsʹ౉͢ StateΛ؅ཧ͠ɺϥΠϑλΠϜΛද͢ίϯςφ Store

  50. Store private let store = Store<UserState>(state: .init(), mutations: .init(), executor:

    .queue(.global())) store.computed.countText.bind(to: countLabel, \.text) store.computed.cellModels.signal.bind(to: tableView, on: .queue(.main)) { tableView, _ in tableView.reloadData() } store.actions.search(query: "GitHub")
  51. Store private let store = Store<UserState>(state: .init(), mutations: .init(), executor:

    .queue(.global())) store.computed.countText.bind(to: countLabel, \.text) store.computed.cellModels.signal.bind(to: tableView, on: .queue(.main)) { tableView, _ in tableView.reloadData() } store.actions.search(query: "GitHub") ॳظԽ࣌ʹStateͱMutationsΛ౉͢ ͜ͷͱ͖౉͢Executor͕MutationsͰͷঢ়ଶมߋͷContextΛܾఆ͢Δ
  52. Store private let store = Store<UserState>(state: .init(), mutations: .init(), executor:

    .queue(.global())) store.computed.countText.bind(to: countLabel, \.text) store.computed.cellModels.signal.bind(to: tableView, on: .queue(.main)) { tableView, _ in tableView.reloadData() } store.actions.search(query: "GitHub") Computedͷ஋ΛViewʹbind͓ͯ͘͠
  53. Store private let store = Store<UserState>(state: .init(), mutations: .init(), executor:

    .queue(.global())) store.computed.countText.bind(to: countLabel, \.text) store.computed.cellModels.signal.bind(to: tableView, on: .queue(.main)) { tableView, _ in tableView.reloadData() } store.actions.search(query: "GitHub") StoreͷΠϯελϯε͕࣋ͭActionsͷؔ਺Λ࣮ߦ͢Δ͜ͱͰɺ ݸผʹߋ৽Ͱ͖Δ
  54. Store private let store = Store<UserState>(state: .init(), mutations: .init(), executor:

    .queue(.global())) store.computed.countText.bind(to: countLabel, \.text) store.computed.cellModels.signal.bind(to: tableView, on: .queue(.main)) { tableView, _ in tableView.reloadData() } Store<UserState>.actions.search(query: "GitHub") staticͳActions͔Βؔ਺Λ࣮ߦ͢ΔͱΞϓϦͷͲ͔͜ΒͰ΋ Store<UserState>ܕͷશͯͷΠϯελϯεʹActionΛbroadcastͰ͖Δ
  55. Example App https://github.com/ra1028/VueFluxExample-GitHub ra1028/VueFluxExample-GitHub

  56. VueFlux • ঢ়ଶετΞͷ໾ׂΛߋʹࡉ෼Խͯ͠ݟ௨͠ͷྑ͍ߏ଄ • ViewModelͷΑ͏ͳը໘ͷঢ়ଶ͔ΒɺΞϓϦશମͷঢ়ଶ·Ͱ StoreͷϥΠϑλΠϜͰදݱ͢Δ • MVVMͱFluxͷσʔλϑϩʔΛ૊Έ߹ΘͤͯɺiOSͰͷ࣮૷ʹ าΈدͬͨFlux

  57. None
  58. None
  59. ·ͱΊ • MVVM͸σʔλϑϩʔͱɺϨΠϠ෼ׂͷ૒ํʹ໰୊͕͋Δ • Fluxͷ࢓૊Έ͸σʔλϑϩʔ͕༏Ε͍ͯΔ͕ɺݒ೦΋͋Δ • Redux͸ ͔͠͠iOSʹ͸߹Θͳ͍Ұ໘΋ • VueFlux͸ֵ৽త͡Όͳ͘ɺطଘͷΞʔΩςΫνϟͷऑ఺

    Λิͬͨ࢓૊ΈΛఏڙ͢Δ
  60. Thanks