Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Flux_with_RxSwift.pdf
Search
Yuji Hato
October 15, 2016
Technology
190
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Flux_with_RxSwift.pdf
Yuji Hato
October 15, 2016
More Decks by Yuji Hato
See All by Yuji Hato
ABEMAにおける 生成AI活用の現在地 / The Current Status of Generative AI at ABEMA
dekatotoro
1
1.2k
ABEMAモバイルアプリ開発のDevOps戦略
dekatotoro
1
730
Multiplatform Engineering Roadmap for the Future
dekatotoro
1
160
Introduction to RIBs
dekatotoro
5
1.4k
継続的な開発スタイル 「AbemaTV iOSアプリを週一で リリースしている話」
dekatotoro
6
4.3k
iOS Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
dekatotoro
0
540
動画アプリをなめらかに動かす技術 - iOS -
dekatotoro
0
500
5分で学ぶ差分更新とRxDataSources
dekatotoro
0
370
AbemaTV モバイルアプリの開発体制と 開発プロセスの話
dekatotoro
0
290
Other Decks in Technology
See All in Technology
Kiro Ambassador を目指す話
k_adachi_01
0
110
2026TECHFRESH畢業分享會 - AI 時代的人生存檔點
line_developers_tw
PRO
0
1.2k
10年間のブログ発信を振り返って見えたWebアプリケーションエンジニアとしての軌跡
stefafafan
0
150
Claude Codeをどのように キャッチアップしているか
oikon48
13
8.5k
SteampipeとExcel Power QueryでAWS構成定義書の作成を自動化する
jhashimoto
0
140
LayerXにおけるセキュリティ管理の現在地と次の一手
tosho
0
240
就職⽀援サービスにおけるキャリアアドバイザーのシフトスケジューリング
recruitengineers
PRO
1
150
マルチアカウント環境での コーディングエージェントを使った障害調査が大変なので AIエージェントにReadOnly権限を付与してみた / ReadOnly AI Agents for Multi-Account AWS Incident Response
yamaguchitk333
2
110
MCP Appsを作ってみよう
iwamot
PRO
4
690
IaC コードを資産へ:AWS CDK 社内ライブラリと横断展開 / aws-summit-japan-2026
gotok365
2
700
小さく始める AI 活用推進 ― 日経電子版 Web チームの事例/nikkei-tech-talk47
nikkei_engineer_recruiting
0
300
2026 TECHFRESH 畢業分享會 - 開發日常大解密!從領域驅動到企業級上線
line_developers_tw
PRO
0
1.2k
Featured
See All Featured
Building the Perfect Custom Keyboard
takai
2
800
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
460
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
210
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
400
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
150
So, you think you're a good person
axbom
PRO
2
2.1k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
65
56k
Designing Powerful Visuals for Engaging Learning
tmiket
1
420
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Transcript
Flux with RxSwift AbemaTV Developer Conference 2016 Yuji Hato
About me Yuji Hato CyberAgent, Inc. / AbemaTV, Inc. dekatotoro
@dekatotoro Contributed services
What is Flux?
What is Flux? IUUQTGBDFCPPLHJUIVCJPqVYEPDTPWFSWJFXIUNM “Data in a Flux application flows
in a single direction”
What is Flux? IUUQTHJUIVCDPNGBDFCPPLqVY
What is Flux? IUUQTHJUIVCDPNGBDFCPPLqVY
What is Flux? IUUQTHJUIVCDPNGBDFCPPLqVY Observer ύλʔϯ !
Why Flux?
Why Flux? ࡢࠓͷΞϓϦ։ൃෳࡶԽ ͷҰ్Λḷ͓ͬͯΓঢ়ଶཧ ͕େม
Why Flux? AbemaTVͷঢ়ଶཧ: Cast ΦϯσϚϯυ ՝ۚ CM Filler ը࣭ ࢹௌ༧
etc… ൪ Fresh ίϝϯτ ࢹௌ FullScreen Feed
Why Flux? ͭΒ͍…
Why Flux? UIૢ࡞ɾ࣌ؒʹ͏ෳࡶͳ ঢ়ଶભҠΛ͔Γ͍ͨ͘͢͠
Why Flux? ViewؒͷґଘؔΛݮΒ͍ͨ͠
MVVM MVC DDD Flux Clean Architecture MVP Why Flux? Flux͕͍ͯͦ͏…?
Flux with RxSwift
Flux with RxSwift Action Dispatcher User Interactions API DB ViewController
View Store Events Events
Flux with RxSwift Event StreamͰܨ͍͡Ό͓͏ͥ
Flux with RxSwift Action Dispatcher User Interactions API DB ViewController
View Store Events Events
Flux with RxSwift Dispatcher ViewController View Store Events Events Events
Event̎छྨͷHot ObservableΛ͏
Flux with RxSwift PublishSubject ҰΩϟογϡ͠ͳ͍Subject Events
Flux with RxSwift Variable ۙͷΛ͚̍ͭͩΩϟογϡ͢ΔSubject ※BehaviorSubjectͷwrapper Events
Dispatcher
Dispatcher Action Dispatcher User Interactions API DB ViewController View Store
Events Events
Dispatcher Dispatcherͷϑϩʔ Dispatcher Store Events Events Action Dispatcher Store Store
Events Events Action Action
Dispatcher Action͕dispatch͢Δ Dispatcher Store Store Events Events Action Action on
subscribe
Dispatcher dispatch͞ΕͨΒStore௨ Dispatcher Store Store Events Events Action Action on
subscribe
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
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
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Λൃߦ͠·͢
Dispatcher class SomeDispatcher { static let shared = SomeDispatcher() let
loading = DispatchSubject<Bool>() let error = DispatchSubject<Error>() let someModel = DispatchSubject<SomeModel>() … } DispatcherΫϥε
Dispatcher class SomeDispatcher { static let shared = SomeDispatcher() let
loading = DispatchSubject<Bool>() let error = DispatchSubject<Error>() let someModel = DispatchSubject<SomeModel>() … } ActionTypeͷΘΓʹDispatchSubjectΛෳ༻ҙ ※DispatcherΫϥε༻్͝ͱʹ͚ͯ·͢
Dispatcher func someAction(value: Bool) { … dispatcher.loading.dispatch(value) … } Action͕dispatch
Action
Action Action Dispatcher User Interactions API DB ViewController View Store
Events Events
Action Actionͷϑϩʔ Action Action Dispatcher User Interactions API DB ViewController
View Action Action Dispatcher API DB ViewController View Device User Interactions
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
Action ඞཁͳσʔλΛऔಘ͢Δ Action Action Dispatcher User Interactions API DB ViewController
View Action Action Action Dispatcher API DB ViewController View Device User Interactions
Action UI, Web, DB, DevicesΛ֎෦IFͱͯ͠ଊ͑Δ IUUQTUIMJHIUDPNCMPHVODMFCPCUIFDMFBOBSDIJUFDUVSFIUNM
Action σʔλͷऔಘޙDispatcherྲྀ͢ Action Action Dispatcher User Interactions API DB ViewController
View Action Action Action Dispatcher API DB ViewController View Device User Interactions
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Λ࣮ߦ͢Δྫ
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࣮ߦ
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) } Τϥʔͷ࣌
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) } Ϩεϙϯε͕ฦ͖ͬͯͨ࣌
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) } ࣮ߦ͕ྃͨ࣌͠
Store
Store Action Dispatcher User Interactions API DB ViewController View Store
Events Events
Store Storeͷϑϩʔ Dispatcher Store Store ViewController View Events Events Events
Store DispatcherͷeventΛObserve͢Δ Dispatcher Store Store ViewController View Events Events Events
subscribe on on subscribe
Store DispatcherͷΠϕϯτ͕ྲྀΕͯ͘Δ Store Store ViewController View Events Events Events subscribe
on on subscribe Dispatcher
Store DispatcherͷΠϕϯτΛݕͨ͠ΒσʔλΛߋ৽ Store Store ViewController View Events Events Events subscribe
on on subscribe Dispatcher
Store σʔλΛߋ৽ͨ͠ΒView௨ Store Store ViewController View Events Events Events subscribe
subscribe Dispatcher on on
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Ϋϥε
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
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
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Ϋϥε
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
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ͷpropertyVariableͱPublishSubject
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
View
View Action Dispatcher User Interactions API DB ViewController View Store
Events Events
View Viewͷϑϩʔ Dispatcher Events Store Store ViewController View Events Action
Action Action User Interactions ViewController View Action
View StoreͱUIͷeventΛObserve͢Δ Dispatcher Events Store Store ViewController View Events subscribe
Action Action User Interactions ViewController View Action
View StoreͷΠϕϯτ͕ྲྀΕͯ͘Δ Dispatcher Events Store Store ViewController View Events on
Action Action User Interactions ViewController View Action
View StoreͷΠϕϯτΛݕͨ͠ΒViewΛߋ৽ Dispatcher Events Store Store ViewController View Events on
Action Action User Interactions ViewController View Action
View UIͷeventΛτϦΨʔʹActionΛ࣮ߦ Dispatcher Events Store Store ViewController View Events Action
Action User Interactions ViewController View Action
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
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
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ͷදࣔ/ඇදࣔ
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
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Λ࣮ߦ
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
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
View searchBar.rx.text.asDriver() .throttle(0.3) .distinctUntilChanged() .drive(onNext: { query in SomeAction.someAction(query: query)
}) .addDisposableTo(rx_disposeBag) UIͷeventΛobserve
View searchBar.rx.text.asDriver() .throttle(0.3) .distinctUntilChanged() .drive(onNext: { query in SomeAction.someAction(query: query)
}) .addDisposableTo(rx_disposeBag) searchBarͷtextೖྗΛobserve
View searchBar.rx.text.asDriver() .throttle(0.3) .distinctUntilChanged() .drive(onNext: { query in SomeAction.someAction(query: query)
}) .addDisposableTo(rx_disposeBag) ೖྗ͞Εͨจࣈʹมߋ͕͋ΔຖʹActionΛ࣮ߦ
Flux with RxSwift Action Dispatcher User Interactions API DB ViewController
View Store Events Events
Ұप͠·ͨ͠ʂ Flux with RxSwift
Proposals
Proposals • DispatcherҰͭʹ͍ͨ͠ • StoreSingletonͰͳ͘ɺదͳϥΠϑαΠ Ϋϧʹ͍ͨ͠ • StoreΛread-onlyʹ͍ͨ͠
Conclusion
Conclusion Pros • ෳͷViewController, ViewΛͬͯෳࡶͳঢ়ଶ ཧΛ͢ΔΞϓϦέʔγϣϯʹ͍͍ͯΔ • ։ൃऀͷ࣮͕౷Ұ͞Ε͍͢ • Viewؒͷґଘ͕ؔݮΔ
Conclusion Cons • SingletonͰूதཧͳͷͰɺ͖ݏ͍͋Δ͔ • ׳ΕΔ·Ͱগ͔͔͠Δ • ࣮͕ʹײ͡Δ…?
FluxΈͳͷͰɺΑΓྑ͘͢ΔͨΊʹ νʔϜͰTrial&Errorͯ͠·͢ Flux with RxSwift
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
https://github.com/dekatotoro/FluxWithRxSwiftSample Flux with RxSwift Sample