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
160
新しい恋愛様式への挑戦/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
470
明日から使える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
20260608_Codexの可能性_ノンプログラマー向け_大城追記
doradora09
PRO
0
760
AIコーディングの次。コードレビューと理解負荷を解消して組織の開発生産性を高める
moongift
PRO
0
160
toio・myCobotでフィジカルAIっぽいことを行うための検討(とりあえず調査) / フィジカルAI LT(IoTLTによる開催)
you
PRO
0
280
Invisible to AI? Making TYPO3 Sites Quotable by AI Search Systems
wolfgangwagner
0
120
Cursor Meetup Sapporo - Cursor物語 続編
cocacola917
0
120
オートロックマンションなのに、各部屋は施錠なし!? 攻撃者が組織内ネットワークで大暴れする理由 / The Front Door Is Locked, but the Rooms Are Wide Open: Why Attackers Move Freely Inside Enterprise Networks
nttcom
0
1.5k
取引先から届く 「セキュリティチェックシート」の読み解き方
kamadamakoto
0
120
Service Connect 上のサービスに ECS Service の外側から到達できなかった話
ota1022
1
130
第3回しろおびセキュリティスポンサーセッション
log0417
0
150
名刺メーカーDevグループ 紹介資料
sansan33
PRO
0
1.2k
PLaMo 3.0 Primeの構造化出力サポート
pfn
PRO
0
190
AIエージェントを前提としたプラットフォーム エンジニアリング:GKEで作るAgent-Ready Golden Path
legalontechnologies
PRO
1
200
Featured
See All Featured
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
460
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
390
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
460
ラッコキーワード サービス紹介資料
rakko
1
4.2M
The untapped power of vector embeddings
frankvandijk
2
1.8k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.2k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
230
Joys of Absence: A Defence of Solitary Play
codingconduct
1
430
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
200
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
SEO for Brand Visibility & Recognition
aleyda
0
4.7k
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