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
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
業務でAWSをあまり使っていない私が、AWSへのモチベを保つ方法
smt7174
0
180
Kiro Crew の Bot (続く役目を任せる窓口) | 目指せ Kiro マスター 12
inariku
PRO
2
320
re:Invent2026 事前勉強会 歴史と愉しみ方10分LT編
toshi_atsumi
0
160
詳解 never 型 - ボトム型の性質を深掘り
nealle
0
160
GCASアップデート(202607-202610)
techniczna
1
240
現場を動かすAIエージェント#2_製造業現場でのAI活用から見た、ハマる罠と実践知
kintotechdev
2
200
Sinatra on PicoRuby ローカルでセルフホスト
bash0c7
0
110
お客様の「やりたい」の先を見通す合意形成プロセス ~裏にある課題と不安を解きほぐすコツ~
yukakoseki
0
120
Kiro Crew 入門 (手を離しても進む仕事) | 目指せ Kiro マスター 11
inariku
PRO
4
590
内製アプリとClaudeスキルのセキュリティチェック、 Backlogの課題で回しています。
ayaka_egaaa
0
160
Ebitengine製iOSアプリ公開ボタンを押す会
eiei114
0
110
IoTとは〜? 数字で見るSORACOM UG【SORACOM UG Explorer 2026】
soracom
PRO
0
330
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
96
14k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
320
Paper Plane
katiecoart
PRO
5
55k
Google's AI Overviews - The New Search
badams
0
1.7k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
380
Optimising Largest Contentful Paint
csswizardry
37
4k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
77k
BBQ
matthewcrist
90
10k
Six Lessons from altMBA
skipperchong
29
4.6k
WCS-LA-2024
lcolladotor
0
980
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.2k
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