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
280
「タップル誕生」における開発の変化 / 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
IHV like なユースケースへのOpenID Connect 関連仕様の適用事例
optim
0
330
GopherCon @シアトル に行ってきました
logica0419
0
320
顧客の要望は2次情報である 〜アンテナを張るFDEの構造論〜
noriakioji
3
960
サービス内で複数のOP・ASを連鎖させる(OAuth/OIDC Numa (Immersion) Workshop 2026)
oidfj
PRO
0
320
LLMアプリ、 雰囲気で運用してませんか? 〜LLMOpsの現在地〜
taka_aki
1
340
ClaudeCodeでセキュリティ監視業務を半自動化_1年の運用でわかったAIに任せる設計の5つのポイント
kintotechdev
3
750
AWSとGitHub Actionsの責任境界と 組織で安全に使用する取り組み
nealle
0
200
500名弱規模の組織のPythonプロジェクト(dbt) をどう管理するか?
hiracky16
0
310
Point Cloud as a Foreign Language for Multi-modal Large Language Model
takmin
0
140
サーバー常駐型の 簡易障害調査AI エージェントを作ってみた話
masayoshi
0
230
サイボウズ 開発本部採用ピッチ / Cybozu Engineer Recruit
cybozuinsideout
PRO
12
86k
1000⼈規模のClaude Enterprise運⽤を「Oktaのグループ」と「Slack」に集約する
sansantech
PRO
0
450
Featured
See All Featured
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
73
42k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
How to Think Like a Performance Engineer
csswizardry
28
2.7k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.5k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.5k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
Why Our Code Smells
bkeepers
PRO
340
58k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
A designer walks into a library…
pauljervisheath
211
24k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.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