Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Flux_with_RxSwift.pdf
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Yuji Hato
October 15, 2016
Technology
200
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.3k
ABEMAモバイルアプリ開発のDevOps戦略
dekatotoro
1
800
Multiplatform Engineering Roadmap for the Future
dekatotoro
1
190
Introduction to RIBs
dekatotoro
5
1.5k
継続的な開発スタイル 「AbemaTV iOSアプリを週一で リリースしている話」
dekatotoro
6
4.3k
iOS Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
dekatotoro
0
560
動画アプリをなめらかに動かす技術 - iOS -
dekatotoro
0
520
5分で学ぶ差分更新とRxDataSources
dekatotoro
0
390
AbemaTV モバイルアプリの開発体制と 開発プロセスの話
dekatotoro
0
290
Other Decks in Technology
See All in Technology
新卒エンジニア、 介護士になる
ryu1013
1
670
複雑な業務システムを高速でつくるため、チーム全員をつくれる人へ
ayakurabuchi
0
120
あなたのDartコードは丸見えかもしれない ~reFlutterで暴くFlutterアプリの中身と多層防御~
zozotech
PRO
0
750
【Oracle AI SPOTLIGHT ウェビナー】事例から見る!なぜOCIが選ばれたのか ― AI時代のクラウド戦略を紐解く ―
oracle4engineer
PRO
1
150
AIがGISを代替する日:〜すべてをAIが創る時代に、私たちがFOSS4Gに関わる意味〜
lehupa
0
200
E2Eってつらくないですか?E2Eの管理コストを下げる手段とその実践
akaboshinit
6
2.9k
eBPF 簡介 2026 版
shunghsiyu
0
160
なぜ東京にいる私が、新潟の技術コミュニティを運営するのか by 八雲慎之助
yakumo
0
210
慶應義塾大学 AI実践05 論文執筆の仕組み化
keio_smilab
PRO
6
3.4k
LLMの知識とAIエージェント
happysamurai294
0
150
構造で守らせる データ・法務・セキュリティ・開発チームとつくる AI時代のデータガバナンスとデータ基盤のリアル
legalontechnologies
PRO
3
750
AI Search (Foundry IQ) におけるRAGデータの準備と配置
ryomaru0825
1
140
Featured
See All Featured
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
Scaling GitHub
holman
464
140k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
810
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
290
StorybookのUI Testing Handbookを読んだ
zakiyama
31
7k
Skip the Path - Find Your Career Trail
mkilby
1
280
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
890
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
730
Building Applications with DynamoDB
mza
96
7.3k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
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