Flux_with_RxSwift.pdf

F01523d30d0f0b7c154914a886406bc4?s=47 Yuji Hato
October 15, 2016

 Flux_with_RxSwift.pdf

F01523d30d0f0b7c154914a886406bc4?s=128

Yuji Hato

October 15, 2016
Tweet

Transcript

  1. Flux with RxSwift AbemaTV Developer Conference 2016 Yuji Hato

  2. About me Yuji Hato CyberAgent, Inc. / AbemaTV, Inc. dekatotoro

    @dekatotoro Contributed services
  3. What is Flux?

  4. What is Flux? IUUQTGBDFCPPLHJUIVCJPqVYEPDTPWFSWJFXIUNM “Data in a Flux application flows

    in a single direction”
  5. What is Flux? IUUQTHJUIVCDPNGBDFCPPLqVY

  6. What is Flux? IUUQTHJUIVCDPNGBDFCPPLqVY

  7. What is Flux? IUUQTHJUIVCDPNGBDFCPPLqVY Observer ύλʔϯ !

  8. Why Flux?

  9. Why Flux? ࡢࠓͷΞϓϦ։ൃ͸ෳࡶԽ ͷҰ్Λḷ͓ͬͯΓঢ়ଶ؅ཧ ͕େม

  10. Why Flux? AbemaTVͷঢ়ଶ؅ཧ: Cast ΦϯσϚϯυ ՝ۚ CM Filler ը࣭ ࢹௌ༧໿

    etc… ൪૊ Fresh ίϝϯτ ࢹௌ਺ FullScreen Feed
  11. Why Flux? ͭΒ͍…

  12. Why Flux? UIૢ࡞ɾ࣌ؒʹ൐͏ෳࡶͳ ঢ়ଶભҠΛ෼͔Γ΍͍ͨ͘͢͠

  13. Why Flux? Viewؒͷґଘؔ܎ΛݮΒ͍ͨ͠

  14. MVVM MVC DDD Flux Clean Architecture MVP Why Flux? Flux͕޲͍ͯͦ͏…?

  15. Flux with RxSwift

  16. Flux with RxSwift Action Dispatcher User Interactions API DB ViewController

    View Store Events Events
  17. Flux with RxSwift Event StreamͰܨ͍͡Ό͓͏ͥ

  18. Flux with RxSwift Action Dispatcher User Interactions API DB ViewController

    View Store Events Events
  19. Flux with RxSwift Dispatcher ViewController View Store Events Events Events

    Event͸̎छྨͷHot ObservableΛ࢖͏
  20. Flux with RxSwift PublishSubject Ұ੾Ωϟογϡ͠ͳ͍Subject Events

  21. Flux with RxSwift Variable ௚ۙͷ஋Λ͚̍ͭͩΩϟογϡ͢ΔSubject ※BehaviorSubjectͷwrapper Events

  22. Dispatcher

  23. Dispatcher Action Dispatcher User Interactions API DB ViewController View Store

    Events Events
  24. Dispatcher Dispatcherͷϑϩʔ Dispatcher Store Events Events Action Dispatcher Store Store

    Events Events Action Action
  25. Dispatcher Action͕dispatch͢Δ Dispatcher Store Store Events Events Action Action on

    subscribe
  26. Dispatcher dispatch͞ΕͨΒStore΁௨஌ Dispatcher Store Store Events Events Action Action on

    subscribe
  27. Dispatcher class DispatchSubject<Element>: ObservableType, ObserverType { typealias E = Element

    fileprivate let subject = PublishSubject<E>() func dispatch(_ value: E) { on(.next(value)) } func on(_ event: Event<E>) { subject.on(event) } func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E { return subject.subscribe(observer) } } Dispatcher༻ͷDispatchSubject
  28. Dispatcher class DispatchSubject<Element>: ObservableType, ObserverType { typealias E = Element

    fileprivate let subject = PublishSubject<E>() func dispatch(_ value: E) { on(.next(value)) } func on(_ event: Event<E>) { subject.on(event) } func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E { return subject.subscribe(observer) } } PublishSubjectͷWrapper
  29. Dispatcher class DispatchSubject<Element>: ObservableType, ObserverType { typealias E = Element

    fileprivate let subject = PublishSubject<E>() func dispatch(_ value: E) { on(.next(value)) } func on(_ event: Event<E>) { subject.on(event) } func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E { return subject.subscribe(observer) } } dispatch͢ΔͱeventΛൃߦ͠·͢
  30. Dispatcher class SomeDispatcher { static let shared = SomeDispatcher() let

    loading = DispatchSubject<Bool>() let error = DispatchSubject<Error>() let someModel = DispatchSubject<SomeModel>() … } DispatcherΫϥε
  31. Dispatcher class SomeDispatcher { static let shared = SomeDispatcher() let

    loading = DispatchSubject<Bool>() let error = DispatchSubject<Error>() let someModel = DispatchSubject<SomeModel>() … } ActionTypeͷ୅ΘΓʹDispatchSubjectΛෳ਺༻ҙ ※DispatcherΫϥε΋༻్͝ͱʹ෼͚ͯ·͢
  32. Dispatcher func someAction(value: Bool) { … dispatcher.loading.dispatch(value) … } Action͕dispatch

  33. Action

  34. Action Action Dispatcher User Interactions API DB ViewController View Store

    Events Events
  35. Action Actionͷϑϩʔ Action Action Dispatcher User Interactions API DB ViewController

    View Action Action Dispatcher API DB ViewController View Device User Interactions
  36. Action View͔ΒActionΛ࣮ߦ Action Action Dispatcher User Interactions API DB ViewController

    View Action Action Dispatcher API DB ViewController View Device User Interactions Action Action Dispatcher User Interactions API DB ViewController View Action Action Action Dispatcher API DB ViewController View Device User Interactions
  37. Action ඞཁͳσʔλΛऔಘ͢Δ Action Action Dispatcher User Interactions API DB ViewController

    View Action Action Action Dispatcher API DB ViewController View Device User Interactions
  38. Action UI, Web, DB, DevicesΛ֎෦IFͱͯ͠ଊ͑Δ IUUQTUIMJHIUDPNCMPHVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM

  39. Action σʔλͷऔಘޙDispatcher΁ྲྀ͢ Action Action Dispatcher User Interactions API DB ViewController

    View Action Action Action Dispatcher API DB ViewController View Device User Interactions
  40. Action func someAction(query: String) { dispatcher.loading.dispatch(true) API.getSome(with: query) .do(onError: {

    [unowned self] error in self.dispatcher.error.dispatch(error) self.dispatcher.loading.dispatch(false) }) .do(onCompleted: { [unowned self] error in self.dispatcher.loading.dispatch(false) }) .subscribe(onNext: { [unowned self] response in let someModel = SomeModel.make(from: response) self.dispatcher.someModel.dispatch(someModel) }) .addDisposableTo(disposeBag) } APIΛ࣮ߦ͢Δྫ
  41. Action func someAction(query: String) { dispatcher.loading.dispatch(true) API.getSome(with: query) .do(onError: {

    [unowned self] error in self.dispatcher.error.dispatch(error) self.dispatcher.loading.dispatch(false) }) .do(onCompleted: { [unowned self] error in self.dispatcher.loading.dispatch(false) }) .subscribe(onNext: { [unowned self] response in let someModel = SomeModel.make(from: response) self.dispatcher.someModel.dispatch(someModel) }) .addDisposableTo(disposeBag) } ϩʔσΟϯάͯ͠API࣮ߦ
  42. Action func someAction(query: String) { dispatcher.loading.dispatch(true) API.getSome(with: query) .do(onError: {

    [unowned self] error in self.dispatcher.error.dispatch(error) self.dispatcher.loading.dispatch(false) }) .do(onCompleted: { [unowned self] error in self.dispatcher.loading.dispatch(false) }) .subscribe(onNext: { [unowned self] response in let someModel = SomeModel.make(from: response) self.dispatcher.someModel.dispatch(someModel) }) .addDisposableTo(disposeBag) } Τϥʔͷ࣌
  43. Action func someAction(query: String) { dispatcher.loading.dispatch(true) API.getSome(with: query) .do(onError: {

    [unowned self] error in self.dispatcher.error.dispatch(error) self.dispatcher.loading.dispatch(false) }) .do(onCompleted: { [unowned self] error in self.dispatcher.loading.dispatch(false) }) .subscribe(onNext: { [unowned self] response in let someModel = SomeModel.make(from: response) self.dispatcher.someModel.dispatch(someModel) }) .addDisposableTo(disposeBag) } Ϩεϙϯε͕ฦ͖ͬͯͨ࣌
  44. Action func someAction(query: String) { dispatcher.loading.dispatch(true) API.getSome(with: query) .do(onError: {

    [unowned self] error in self.dispatcher.error.dispatch(error) self.dispatcher.loading.dispatch(false) }) .do(onCompleted: { [unowned self] error in self.dispatcher.loading.dispatch(false) }) .subscribe(onNext: { [unowned self] response in let someModel = SomeModel.make(from: response) self.dispatcher.someModel.dispatch(someModel) }) .addDisposableTo(disposeBag) } ࣮ߦ͕׬ྃͨ࣌͠
  45. Store

  46. Store Action Dispatcher User Interactions API DB ViewController View Store

    Events Events
  47. Store Storeͷϑϩʔ Dispatcher Store Store ViewController View Events Events Events

  48. Store DispatcherͷeventΛObserve͢Δ Dispatcher Store Store ViewController View Events Events Events

    subscribe on on subscribe
  49. Store DispatcherͷΠϕϯτ͕ྲྀΕͯ͘Δ Store Store ViewController View Events Events Events subscribe

    on on subscribe Dispatcher
  50. Store DispatcherͷΠϕϯτΛݕ஌ͨ͠ΒσʔλΛߋ৽ Store Store ViewController View Events Events Events subscribe

    on on subscribe Dispatcher
  51. Store σʔλΛߋ৽ͨ͠ΒView΁௨஌ Store Store ViewController View Events Events Events subscribe

    subscribe Dispatcher on on
  52. Flux: Store class Store { let disposeBag = DisposeBag() func

    bind<O, E>(_ observable: O, _ param: Variable<E>) where O: ObservableType, E == O.E { observable.bindTo(param).addDisposableTo(disposeBag) } func bind<O, E>(_ observable: O, _ param: PublishSubject<E>) where O: ObservableType, E == O.E { observable.bindTo(param).addDisposableTo(disposeBag) } } Store਌Ϋϥε
  53. Flux: Store class Store { let disposeBag = DisposeBag() func

    bind<O, E>(_ observable: O, _ param: Variable<E>) where O: ObservableType, E == O.E { observable.bindTo(param).addDisposableTo(disposeBag) } func bind<O, E>(_ observable: O, _ param: PublishSubject<E>) where O: ObservableType, E == O.E { observable.bindTo(param).addDisposableTo(disposeBag) } } Variable༻ͷbind
  54. Flux: Store class Store { let disposeBag = DisposeBag() func

    bind<O, E>(_ observable: O, _ param: Variable<E>) where O: ObservableType, E == O.E { observable.bindTo(param).addDisposableTo(disposeBag) } func bind<O, E>(_ observable: O, _ param: PublishSubject<E>) where O: ObservableType, E == O.E { observable.bindTo(param).addDisposableTo(disposeBag) } } PublishSubject༻ͷbind
  55. Flux: Store class SomeStore: Store { static let shared =

    SomeStore() let loading = Variable<Bool>(false) let error = PublishSubject<Error>() let someModel = Variable<SomeModel>(SomeModel()) init(dispatcher: SomeDispatcher = .shared) { super.init() bind(dispatcher.loading, loading) bind(dispatcher.error, error) bind(dispatcher.someModel, someModel) } } StoreΫϥε
  56. Flux: Store class SomeStore: Store { static let shared =

    SomeStore() let loading = Variable<Bool>(false) let error = PublishSubject<Error>() let someModel = Variable<SomeModel>(SomeModel()) init(dispatcher: SomeDispatcher = .shared) { super.init() bind(dispatcher.loading, loading) bind(dispatcher.error, error) bind(dispatcher.someModel, someModel) } } StoreΫϥε͸Singleton
  57. Flux: Store class SomeStore: Store { static let shared =

    SomeStore() let loading = Variable<Bool>(false) let error = PublishSubject<Error>() let someModel = Variable<SomeModel>(SomeModel()) init(dispatcher: SomeDispatcher = .shared) { super.init() bind(dispatcher.loading, loading) bind(dispatcher.error, error) bind(dispatcher.someModel, someModel) } } Storeͷproperty͸VariableͱPublishSubject
  58. Flux: Store class SomeStore: Store { static let shared =

    SomeStore() let loading = Variable<Bool>(false) let error = PublishSubject<Error>() let someModel = Variable<SomeModel>(SomeModel()) init(dispatcher: SomeDispatcher = .shared) { super.init() bind(dispatcher.loading, loading) bind(dispatcher.error, error) bind(dispatcher.someModel, someModel) } } initͰdispatcherͱbind
  59. View

  60. View Action Dispatcher User Interactions API DB ViewController View Store

    Events Events
  61. View Viewͷϑϩʔ Dispatcher Events Store Store ViewController View Events Action

    Action Action User Interactions ViewController View Action
  62. View StoreͱUIͷeventΛObserve͢Δ Dispatcher Events Store Store ViewController View Events subscribe

    Action Action User Interactions ViewController View Action
  63. View StoreͷΠϕϯτ͕ྲྀΕͯ͘Δ Dispatcher Events Store Store ViewController View Events on

    Action Action User Interactions ViewController View Action
  64. View StoreͷΠϕϯτΛݕ஌ͨ͠ΒViewΛߋ৽ Dispatcher Events Store Store ViewController View Events on

    Action Action User Interactions ViewController View Action
  65. View UIͷeventΛτϦΨʔʹActionΛ࣮ߦ Dispatcher Events Store Store ViewController View Events Action

    Action User Interactions ViewController View Action
  66. View store.loading.asObservable() .distinctUntilChanged() .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] loading in

    self.loadingView.hidden(!loading) }) .addDisposableTo(rx_disposeBag) store.error .subscribe(onNext: { error in ErrorAction.show(.apiError(error)) }) .addDisposableTo(rx_disposeBag) store.someModel.asObservable() .map { $0.dataSource } .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] dataSource in self.dataSource.value = dataSource self.tableView?.reloadData() }) .addDisposableTo(rx_disposeBag) Storeͷobserve
  67. View store.loading.asObservable() .distinctUntilChanged() .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] loading in

    self.loadingView.hidden(!loading) }) .addDisposableTo(rx_disposeBag) store.error .subscribe(onNext: { error in ErrorAction.show(.apiError(error)) }) .addDisposableTo(rx_disposeBag) store.someModel.asObservable() .map { $0.dataSource } .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] dataSource in self.dataSource.value = dataSource self.tableView?.reloadData() }) .addDisposableTo(rx_disposeBag) StoreͷloadingΛobserve
  68. View store.loading.asObservable() .distinctUntilChanged() .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] loading in

    self.loadingView.hidden(!loading) }) .addDisposableTo(rx_disposeBag) store.error .subscribe(onNext: { error in ErrorAction.show(.apiError(error)) }) .addDisposableTo(rx_disposeBag) store.someModel.asObservable() .map { $0.dataSource } .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] dataSource in self.dataSource.value = dataSource self.tableView?.reloadData() }) .addDisposableTo(rx_disposeBag) loadingViewͷදࣔ/ඇදࣔ
  69. View store.loading.asObservable() .distinctUntilChanged() .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] loading in

    self.loadingView.hidden(!loading) }) .addDisposableTo(rx_disposeBag) store.error .subscribe(onNext: { error in ErrorAction.show(.apiError(error)) }) .addDisposableTo(rx_disposeBag) store.someModel.asObservable() .map { $0.dataSource } .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] dataSource in self.dataSource.value = dataSource self.tableView?.reloadData() }) .addDisposableTo(rx_disposeBag) StoreͷerrorΛobserve
  70. View store.loading.asObservable() .distinctUntilChanged() .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] loading in

    self.loadingView.hidden(!loading) }) .addDisposableTo(rx_disposeBag) store.error .subscribe(onNext: { error in ErrorAction.show(.apiError(error)) }) .addDisposableTo(rx_disposeBag) store.someModel.asObservable() .map { $0.dataSource } .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] dataSource in self.dataSource.value = dataSource self.tableView?.reloadData() }) .addDisposableTo(rx_disposeBag) ErrorActionΛ࣮ߦ
  71. View store.loading.asObservable() .distinctUntilChanged() .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] loading in

    self.loadingView.hidden(!loading) }) .addDisposableTo(rx_disposeBag) store.error .subscribe(onNext: { error in ErrorAction.show(.apiError(error)) }) .addDisposableTo(rx_disposeBag) store.someModel.asObservable() .map { $0.dataSource } .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] dataSource in self.dataSource.value = dataSource self.tableView?.reloadData() }) .addDisposableTo(rx_disposeBag) StoreͷsomeModelΛobserve
  72. View store.loading.asObservable() .distinctUntilChanged() .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] loading in

    self.loadingView.hidden(!loading) }) .addDisposableTo(rx_disposeBag) store.error .subscribe(onNext: { error in ErrorAction.show(.apiError(error)) }) .addDisposableTo(rx_disposeBag) store.someModel.asObservable() .map { $0.dataSource } .observeOn(MainScheduler.instance) .subscribe(onNext: { [unowned self] dataSource in self.dataSource.value = dataSource self.tableView?.reloadData() }) .addDisposableTo(rx_disposeBag) tableViewΛreload
  73. View searchBar.rx.text.asDriver() .throttle(0.3) .distinctUntilChanged() .drive(onNext: { query in SomeAction.someAction(query: query)

    }) .addDisposableTo(rx_disposeBag) UIͷeventΛobserve
  74. View searchBar.rx.text.asDriver() .throttle(0.3) .distinctUntilChanged() .drive(onNext: { query in SomeAction.someAction(query: query)

    }) .addDisposableTo(rx_disposeBag) searchBarͷtextೖྗΛobserve
  75. View searchBar.rx.text.asDriver() .throttle(0.3) .distinctUntilChanged() .drive(onNext: { query in SomeAction.someAction(query: query)

    }) .addDisposableTo(rx_disposeBag) ೖྗ͞Εͨจࣈʹมߋ͕͋ΔຖʹActionΛ࣮ߦ
  76. Flux with RxSwift Action Dispatcher User Interactions API DB ViewController

    View Store Events Events
  77. Ұप͠·ͨ͠ʂ Flux with RxSwift

  78. Proposals

  79. Proposals • Dispatcher͸Ұͭʹ͍ͨ͠ • Store͸SingletonͰͳ͘ɺద੾ͳϥΠϑαΠ Ϋϧʹ͍ͨ͠ • StoreΛread-onlyʹ͍ͨ͠

  80. Conclusion

  81. Conclusion Pros • ෳ਺ͷViewController, ViewΛ࢖ͬͯෳࡶͳঢ়ଶ ؅ཧΛ͢ΔΞϓϦέʔγϣϯʹ޲͍͍ͯΔ • ։ൃऀͷ࣮૷͕౷Ұ͞Ε΍͍͢ • Viewؒͷґଘؔ܎͕ݮΔ

  82. Conclusion Cons • SingletonͰूத؅ཧͳͷͰɺ޷͖ݏ͍͋Δ͔΋ • ׳ΕΔ·Ͱগ͔͔͠Δ • ࣮૷͕৑௕ʹײ͡Δ…?

  83. Flux͸࿮૊ΈͳͷͰɺΑΓྑ͘͢ΔͨΊʹ νʔϜͰTrial&Errorͯ͠·͢ Flux with RxSwift

  84. Thank you ࢀߟࢿྉ https://facebook.github.io/flux/ https://github.com/facebook/flux https://github.com/thoughtbot/Delta https://github.com/yonekawa/SwiftFlux https://speakerdeck.com/ogaclejapan/flux-de-relax

  85. https://github.com/dekatotoro/FluxWithRxSwiftSample Flux with RxSwift Sample