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
マッチングアプリにつきまとう状態管理のつらさ/torte_state
Search
corin8823
October 18, 2017
Technology
7.4k
9
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
マッチングアプリにつきまとう状態管理のつらさ/torte_state
corin8823
October 18, 2017
More Decks by corin8823
See All by corin8823
検証と資産化を形にするプロダクト組織へ/tapple_pmconf2024
corin8823
2
17k
経済学の知見を活かしたユーザに行動変容を促す仕掛け - タップルでの共同研究プロジェクトを例として
corin8823
1
170
新しい恋愛様式への挑戦/engineer_career_design_week
corin8823
0
200
安心安全な開発にむけて/matching-dev-meetup-5
corin8823
0
470
Bad practice for tax hike handling
corin8823
0
620
Practice of build and CI/CD at tapple/practice_of_build_at_tapple
corin8823
1
290
「タップル誕生」における開発の変化 / change_development
corin8823
1
480
明日から使えるViewControllerの Memory Leak検出/iOSDC-2018-Memory-Leak
corin8823
6
2.8k
Introduction Differ/differ
corin8823
0
3.8k
Other Decks in Technology
See All in Technology
Point Cloud as a Foreign Language for Multi-modal Large Language Model
takmin
0
360
Bet AI Day 2026丨Production-Ready AI Agents — エンタープライズの実務を任せるための設計と運用
layerx
PRO
2
1.6k
書籍『生成AIの安全性入門』の入門
wataoka
0
150
The Django UUID Story - DjangoCon US 2026
pauloxnet
0
380
Introduction to Sansan Meishi Maker Development Engineer
sansan33
PRO
0
470
指示待ちから変化に応じるClaude Codeへ!~環境からAgentへの帰り道を作る~
gotalab555
9
1.8k
PM領域でのAI Agentの活用
lycorptech_jp
PRO
0
210
Does an AI Watermark Survive Translation?
machinetranslation
0
520
Backstageでつくるセルフサービスな社内開発基盤
kikunosuke75
1
180
「ミスを許さない手順書」を作ってみた 〜 個人的にはこれ以上できることはあまりなさそう/20260827-ssmjp-operation-procedure-update
opelab
17
17k
『自分で判断できるか』を基準に、プロダクトのハンズオン研修でAI利用の線を引いてみた / Where We Drew the Line on AI in Hands-on Training
honyanya
1
790
Hub & Spoke 環境のネットワークルーティングを分解してみる
tsuyataku
1
500
Featured
See All Featured
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Optimising Largest Contentful Paint
csswizardry
37
3.9k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
For a Future-Friendly Web
brad_frost
183
10k
How Software Deployment tools have changed in the past 20 years
geshan
1
34k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
850
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
300
HDC tutorial
michielstock
2
830
WCS-LA-2024
lcolladotor
0
820
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
500
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
Transcript
ϚονϯάΞϓϦʹ͖ͭ·ͱ͏ঢ়ଶཧͷͭΒ͞ ߴڮ ༏հ @corin8823 גࣜձࣾτϧς / Developer 2017.10.18 CA.SWIFT #4
ߴڮ ༏հ yusuke takahashi גࣜձࣾτϧς / Developer 2013৽ଔೖࣾ corin8823 @corin8823
ࣗݾհ
IUUQTTXFFUUPSUFDPN ࡀҎ্ݶఆ
ϚονϯάΞϓϦʹ͖ͭ·ͱ͏ ঢ়ଶཧͷͭΒ͞
7JFX$POUSPMMFSΛ ލ͍ͩঢ়ଶཧ
7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ ߘ༰ ະಡطಡ ભҠ
w ৄࡉͰʮ͍͍Ͷʯͨ࣌͠ʹҰཡͷө w "λϒͰʮ͍͍Ͷʯͨ࣌͠ʹ#λϒͰͷө w ৄࡉͰআͨ࣌͠ʹҰཡ͔Βফ͢ w ʑ 7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ
લճͷొஃࢿྉ
IUUQTGBDFCPPLHJUIVCJPqVYEPDTJOEFQUIPWFSWJFXIUNMDPOUFOU 'MVY
7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ ߘ༰ ະಡطಡ ભҠ
final class TopAction { enum Item { case load case
error } private let dispatcher: Dispatcher init(dispatcher: Dispatcher = .shared) { self.dispatcher = dispatcher } func loadItem() { let req = API.ItemRequest() Session.send(req) { result in switch result { case .success(let items): self.dispatcher.dispatch(obj: items, key: Item.load) case .failure(let error): self.dispatcher.dispatch(obj: error, key: Item.error) } } } }
final class TopStore { private(set) var items = Variable<[Item]>([]) private(set)
var error = PublishSubject<Error>() init(dispatcher: Dispatcher = .shared) { dispatcher.register(observer: self, key: TopAction.Item.load) { [weak self] (items: [Item]) in self?.items.value = items } dispatcher.register(observer: self, key: TopAction.Item.error) { [weak self] (error: Error) in self?.error.on(.next(error)) } } }
final class TopViewController: UIViewController { @IBOutlet weak var tableView: UITableView!
private let store = TopStore() private let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() self.store.error .bind { _ in // error handling } .disposed(by: self.disposeBag) self.store.items .asObservable() .bind { [weak self] _ in self?.tableView.reloadData() } .disposed(by: self.disposeBag) TopAction().loadItem() } }
7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ ߘ༰ ະಡطಡ ભҠ
final class TopDetailAction { enum ItemDetail { case update case
error } func update(itemId: Int64) { let req = API.UpdateItemRequest(itemId: id) Session.send(req) { result in switch result { case .success(let item): self.dispatcher.dispatch(obj: item, key: ItemDetail.update) case .failure(let error): self.dispatcher.dispatch(obj: error, key: update.error) } } } }
final class TopDetailAction { enum ItemDetail { case update case
error } enum Item { case update } func update(itemId: Int64) { let req = API.UpdateItemRequest(itemId: id) Session.send(req) { result in switch result { case .success(let item): self.dispatcher.dispatch(obj: item, key: ItemDetail.update) self.dispatcher.dispatch(obj: item, key: Item.update) case .failure(let error): self.dispatcher.dispatch(obj: error, key: update.error) } } } }
final class TopStore { private(set) var items = Variable<[Item]>([]) private(set)
var error = PublishSubject<Error>() init(dispatcher: Dispatcher = .shared) { dispatcher.register(observer: self, key: TopAction.Item.load) { [weak self] (items: [Item]) in self?.items.value = items } dispatcher.register(observer: self, key: TopAction.Item.error) { [weak self] (error: Error) in self?.error.on(.next(error)) } dispatcher.register(observer: self, key: TopDetailAction.Item.update) { [weak self] (item: Item) in if let i = self?.items.value.findFirstIndex ({ $0.id == item.id }) { self?.items.value[i] = match } } } }
final class TopViewController: UIViewController { @IBOutlet weak var tableView: UITableView!
private let store = TopStore() private let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() self.store.error .bind { _ in // error handling } .disposed(by: self.disposeBag) self.store.items .asObservable() .bind { [weak self] _ in self?.tableView.reloadData() } .disposed(by: self.disposeBag) TopAction().loadItem() } }
7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ 5PQ7JFX$POUSPMMFS 5PQ4UPSF 5PQ"DUJPO %JTQBUDIFS
7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ 5PQ%FUBJM7JFX$POUSPMMFS 5PQ%FUBJM4UPSF 5PQ%FUBJM"DUJPO %JTQBUDIFS
7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ 5PQ7JFX$POUSPMMFS 5PQ%FUBJM7JFX$POUSPMMFS 5PQ4UPSF 5PQ%FUBJM4UPSF 5PQ%FUBJM"DUJPO %JTQBUDIFS
ߘͷঝೝ
ߘͷঝೝ Ϣʔβʔ ঝೝऀ αʔόʔ ߘ ཧը໘
ߘͷঝೝ Ϣʔβʔ ঝೝऀ αʔόʔ ঢ়ଶө ঝೝ
w ྸ֬ೝ w χοΫωʔϜ w ࣸਅߘ w ࠷ॳͷϝοηʔδ w ঁੑ͔ΒͷΫνίϛ
ঝೝ͕ඞཁͳߘ
ߘͷঝೝ Ϣʔβʔ ঝೝऀ αʔόʔ ঢ়ଶө ঝೝ
w Ͱ͖Δ͚ͩૣ͘ঝೝΛ͍͑ͨ w ৭ʑͳঝೝ͕͋Δ ࠓޙ૿͑ΔՄೳੑ w ঝೝऔಘ"1*ΛϙʔϦϯά ʀТʀʆ ŲƄƂŕ
w 8FC4PDLFUுΔʁ w ΤϯδχΞ࠷খݶͰ ߘͷঝೝͷ
'JSFCBTF3FBMUJNF%BUBCBTF IUUQTpSFCBTFHPPHMFDPNQSPEVDUTSFBMUJNFEBUBCBTF
IUUQTpSFCBTFHPPHMFDPN 'JSFCBTF
w ϦΞϧλΠϜ w ΦϑϥΠϯ w ΫϥΠΞϯτ͔ΒΞΫηεՄೳ 'JSFCBTF3FBMUJNF%BUBCBTF IUUQTpSFCBTFHPPHMFDPNEPDTEBUBCBTF
ߘͷঝೝϑϩʔ Ϣʔβʔ ঝೝऀ αʔόʔ ঢ়ଶө ঝೝ 4ZOD
w VTFST\VTFS@JEతͳ^CBEHF ۩ମྫ class Badge { var notification: Int var
match: Int var message: Int } w OPUJpDBUJPO͕૿͑ͨΒ࠶ࣗΛऔಘ w NBUDI͕૿͑ͨΒϚονͷϦετΛߋ৽ w NFTTBHF͕૿͑ͨΒόοδΛ͚ͭͯɾɾ
'JSFCBTF3FBMUJNF%BUBCBTFΛͬͨߘͷঝೝ Ϣʔβʔ αʔόʔ 4ZOD ঝೝ
'JSFCBTF3FBMUJNF%BUBCBTFΛͬͨߘͷঝೝ Ϣʔβʔ αʔόʔ όοδΛΠϯΫϦϝϯτ 4ZOD ঝೝ
'JSFCBTF3FBMUJNF%BUBCBTFΛͬͨߘͷঝೝ Ϣʔβʔ αʔόʔ όοδΛΠϯΫϦϝϯτ 4ZOD ঝೝ "1*ίʔϧ
"1*Λୟ͘τϦΨʔ͚ͩΛ 'JSFCBTF3FBMUJNF%BUBCBTFʹ
w ճ͔͑͠ͳ͍ػೳͷΛ͍ΕͨΓ w "1*ୟ͘લʹ7JFXʹө͍ͤͨ͞ w "1*ୟ͘·Ͱͳ͍ ߘͷঝೝҎ֎ʹ
w ճ͔͑͠ͳ͍ػೳͷΛ͍ΕͨΓ w "1*ୟ͘લʹ7JFXʹө͍ͤͨ͞ w "1*ୟ͘·Ͱͳ͍ ߘͷঝೝҎ֎ʹ w ΞΠςϜͷಉظͨ͘͠ͳΔ
w ճ͔͑͠ͳ͍ػೳͷΛ͍ΕͨΓ w "1*ୟ͘લʹ7JFXʹө͍ͤͨ͞ w "1*ୟ͘·Ͱͳ͍ ߘͷঝೝҎ֎ʹ w ΞΠςϜͷಉظͨ͘͠ͳΔ w
͋Ε͜Εɻɻɻ IUUQGVSBOEPOQJHHJUIVCJPGQJH@TBNQMFIPCCZCBE@TQJSBM
·ͱΊ
·ͱΊ ViewControllerΛލ͍ͩঢ়ଶཧ Firebase Realtime DatabaseΛͬͨߘͷঝೝ
7JFX$POUSPMMFSΛލ͍ͩঢ়ଶཧ 5PQ7JFX$POUSPMMFS 5PQ%FUBJM7JFX$POUSPMMFS 5PQ4UPSF 5PQ%FUBJM4UPSF 5PQ%FUBJM"DUJPO %JTQBUDIFS
'JSFCBTF3FBMUJNF%BUBCBTFΛͬͨߘͷঝೝ Ϣʔβʔ ঝೝऀ αʔόʔ ঢ়ଶө ঝೝ 4ZOD
5)"/,:06