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
健康的なMVVM 書いてますか? ~MVVMアンチパターン集~
Search
takasek
April 27, 2016
Programming
21k
26
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
健康的なMVVM 書いてますか? ~MVVMアンチパターン集~
Health Swift Meetup (
http://finc-swift.connpass.com/event/29901/
) の発表資料です。
takasek
April 27, 2016
More Decks by takasek
See All by takasek
影響スケッチでFatViewControllerを可視化してみよう' / 20200530 effect sketch #swiftzoomin
takasek
1
1.8k
Clean Architecture: クライアントアプリの「中心」とは何か / 20200121 the center of the client #ios_ca
takasek
10
3.9k
SOLID原則を生活に適用する / 20190906 iOSDC
takasek
4
13k
APIレスポンスにおける直和型の表現を考える / 20190730 sekkeikaigi
takasek
4
1.7k
Dark Mode / 20190617 #wwdc_rusuban
takasek
11
1.6k
継続渡しと契約による設計 / 20190319 #tryswift_pre
takasek
8
2.2k
Continuation-Passing Style and Design By Contract(English ver.) / 20190319(E) #tryswift_pre
takasek
1
380
FiNCのクライアントアーキテクチャを揃える試み / 20190110 #app_mp
takasek
1
7.5k
SOLID原則のSとDとテストの話 - 「Swiftらしく設計する」Another / 20181221 #roppongiswift
takasek
1
1.4k
Other Decks in Programming
See All in Programming
【加筆修正版】Laravel のアプリケーションをどこにデプロイするか #phpcon_ehime
akase244
0
150
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.5k
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
400
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
150
R8 の設定見直しでアプリサイズ削減
andpad
0
220
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
260
なぜCTOを降りてFDEを選んだのか?〜なぜプロダクト企業がFDEで顧客の現場に踏み込むのか〜
gonta
1
220
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
270
パスワード再設定メールのURLについて知っておこう
uyuki234
0
160
Everything will be SERVERLESS — 信じて運用した10年の経験値 / Everything Will be Serverless — Lessons Learned from 10 Years of Operational Experience
seike460
PRO
1
1.1k
JIS keyboard support in Omarchy
lef237
0
130
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
1.2k
Featured
See All Featured
Music & Morning Musume
bryan
48
7.4k
Chasing Engaging Ingredients in Design
codingconduct
0
360
Everyday Curiosity
cassininazir
0
360
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.4k
Building an army of robots
kneath
307
47k
Utilizing Notion as your number one productivity tool
mfonobong
4
620
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
570
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
77k
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
2
2k
BBQ
matthewcrist
90
10k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
540
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
67k
Transcript
݈߁తͳ.77. ॻ͍ͯ·͔͢ʁ d.77.Ξϯνύλʔϯूd )FBMUI4XJGU.FFUVQ CZ XJUI'J/$
!UBLBTFL w GSFFMBODFJ04&OHJOFFS w 'J/$͞ΜͰ͓ࣄ͍͍ͤͯͨͩͯ͞·͢ w ɹ!UBLBTFL w ɹUBLBTFL w
ɹUBLBTFL
!UBLBTFL w /PUJGXJGU IUUQTHJUIVCDPNUBLBTFL/PUJGXJGU /4/PUJpDBUJPOͷVTFS*OGPΛ 4XJGUZʹѻ͏ϚΠΫϩϥΠϒϥϦ w "DUJPO$MPTVSBCMF IUUQTHJUIVCDPNUBLBTFL"DUJPO$MPTVSBCMF UBSHFUBDUJPOΛ͢ॲཧΛ
4XJGUZͳΫϩʔδϟͰॻ͚Δ ϚΠΫϩϥΠϒϥϦ (JU)VCͰ 044ϥΠϒϥϦ࡞ͬͯ·͢
͓͞Β͍ .77.ͱ
.PEFM 7JFX.PEFM 7JFX %BUB #JOEJOH $PNNBOET ɹ%BUB#JOEJOHͷ࣮ݱͷͨΊʹɺJ04ͷ߹ ɹɹ'31ϥΠϒϥϦ 3Y4XJGU 3FBDUJWF$PDPB
ɹɹσʔλόΠϯσΟϯάϥΠϒϥϦ 4XJGU#POE ͷ ɹɹαϙʔτ͕ඞཁ ɹ ˞'J/$Ͱݱࡏ4XJGU#POEΛར༻
ຊ
͜Ε͔Β ෆ݈߁ͳ7JFX.PEFMΛ ͠·͢
Ͱ͠ΌΓ7JFX.PEFM Χϧςᶃ
class MyViewModel { weak var view: MyView? func doSomething(fuga: Fuga)
{ guard let view = view else { return } if view.isHoge { view.doSomething(fuga) } } } class MyView { func awakeFromNib() { viewModel.view = self } } Ͱ͠ΌΓ7JFX.PEFM ঢ় 7JFX.PEFM͕7JFXͷࢀরΛ࣋ͬͯɺૢ࡞͢Δ
Ͱ͠ΌΓ7JFX.PEFM පࠜ ࠜຊతʹઃܭ͕͓͔͍͠ w 7JFXʹमਖ਼͕ೖͬͨΒ7JFX.PEFMमਖ਼͠ͳ͖Όʜ w ͔ͤͬ͘ͷ.77.ύλʔϯ͕ແͩ͠ʂ w ʜͱ͍͏͔ɺ͜Ε.77.ʹͳͬͯͳ͍
ґଘͷํ 7JFXɹɹ7JFX.PEFM 7JFX.PEFMɹɹ.PEFM Ҿ༻"SDIJUFDUJOH"OESPJEʜ5IFDMFBOXBZ IUUQGFSOBOEPDFKBTDPNBSDIJUFDUJOHBOESPJEUIFDMFBOXBZ 7JFX.PEFM 7JFX σʔλόΠϯσΟϯά
Ͱ͠ΌΓ7JFX.PEFM ॲํᝦ w 7JFX.PEFMࣗࣗͷঢ়ଶΛมߋ͢Δ͚ͩ w Ͳ͏ΘΕΔ͔Ұؔ͠ͳ͍͠ɺ ୭ 7JFX ʹόΠϯυ͞Ε͍ͯΔ͔Βͳ͍ ʹ7JFXʹґଘ͠ͳ͍
ʹมߋʹڧ͍ 7JFX.PEFMˠ7JFX ඞͣόΠϯσΟϯάͰܨ͙
class MyViewModel { let fuga = Observable<Fuga?>(nil) func didReceiveFuga(fuga: Fuga)
{ self.fuga.value = fuga } } class MyView { func awakeFromNib() { viewModel.fuga.ignoreNil().observe { [weak self] in self?.doSomething($0) } } } Ͱ͠ΌΓ7JFX.PEFM վળྫ
ࠞઢ͍ͯ͠Δ 7JFX.PEFM Χϧςᶄ
ࠞઢ͍ͯ͠Δ7JFX.PEFM ঢ় 7JFX.PEFMͷίϚϯυ͕ ɹॲཧͷྃ࣌ʹ࣮ߦ͢ΔΫϩʔδϟΛड͚ͨΓ class MyView { func awakeFromNib() {
viewModel.alertMessage.observe { [weak self] in self?.showAlert($0) // ↓Ͳ͕ͬͪຊے!? } } func didTapButton() { viewModel.doSomething(completion: { [weak self] alertMessage in self?.showAlert(alertMessage) // ↑Ͳ͕ͬͪຊے!? }) } } ˠॲཧͷྲྀΕ͕ΧΦεʹʂ BMFSU.FTTBHF 0CTFSWBCMF4USJOH͕ มߋ͞ΕͨΒൃಈ ͳΜ͔ͬͨ݁Ռ BMFSU.FTTBHFΛ ड͚औΔ
ࠞઢ͍ͯ͠Δ7JFX.PEFM පࠜ ॲཧͷྲྀΕ͕ ཧͰ͖͍ͯͳ͍
ࠞઢ͍ͯ͠Δ7JFX.PEFM ॲํᝦ جຊɺ 7JFXˠ7JFX.PEFMίϚϯυΛୟ͚ͩ͘ 7JFX.PEFMˠ7JFXόΠϯυ͢Δ͚ͩ 7JFX.PEFM 7JFX %BUB #JOEJOH $PNNBOET
ᶃίϚϯυ ᶄঢ়ଶมߋ ᶅঢ়ଶө †એݴతʹهड़
Ψϥεͷ7JFX.PEFM Χϧςᶅ
class MyViewModel { let text = Observable<String>("") let textLength =
Observable<Int>(0) } vm.text.value = "ͳΜ͔͍ςΩετ" vm.text // "ͳΜ͔͍ςΩετ" vm.textLength // 0 ←!!?!!? Ψϥεͷ7JFX.PEFM ঢ় յΕͦ͏ͳ0CTFSWBCMF͔ΓूΊͯ͠·͏
Ψϥεͷ7JFX.PEFM පࠜ ঢ়ଶͷओैؔΛ એݴతʹදݱͰ͖͍ͯͳ͍ w UFYUΛมߋͨ͠Βɺ ͦͷϝιουͰUFYU-FOHUI มߋ͢ΔΑ͏ʹؾΛ͚ͭΔʁ ͍͍ɺͦΜͳͷਓ͕ؒ έΞ͖͢͜ͱ͡Όͳ͍͔Βʂ
ຖճҪށ͔Β ਫΛ·͞ΕͯΔΑ͏ͳ ॏ࿑ಇײ
Ψϥεͷ7JFX.PEFM ॲํᝦ $PMEͳ0CTFSWBCMFΛ͏ )PU 0CTFSWBCMF $PME &WFOU1SPEVDFS
class MyViewModel { let text = Observable<String>("") let textLength: EventProducer<Int>
init() { textLength = text.map { $0.characters.count } } } vm.text // "ͳΜ͔͍ςΩετ" vm.textLength // 9 Ψϥεͷ7JFX.PEFM վળྫ ɹUFYU-FOHUINBQ USBOTGPSNJOHPQFSBUPS Λ௨͚ͩ͢ͷ ɹ$PMEͳଘࡏʹ͠ɺ߹ੑΛอূ એݴతʹهड़Ͱ͖ͨʂ
ઉΒͣͳ 7JFX.PEFM Χϧςᶆ
class MyViewModel { let name = Observable<String>("") let address =
Observable<String>("") } let vm = MyViewModel() vm.name.observe { func setUserData() } vm.address.observe { func setUserData() } func setUserData(userId: Int) { userNameLabel.text = vm.name userAddressLabel.text = vm.address } ઉΒͣͳ7JFX.PEFM ঢ় ɹ7JFX.PEFMͷมԽΛड͚औͬͨ7JFX͕ɺ ɹվΊͯ7JFX.PEFMͷϓϩύςΟΛࢀর͍ͯ͠Δ OBNFͱBEESFTT ͲͪΒ͕มߋ͞Εͯ ྆ํΛͬͯߋ৽ॲཧΛ ߦ͍͍ͨʂ
ઉΒͣͳ7JFX.PEFM පࠜ σʔλΛదͳཻɾͰ ͍ͯ͠ͳ͍
ઉΒͣͳ7JFX.PEFM ॲํᝦ ֤छΦϖϨʔλΛ͍͜ͳ͢ class MyViewModel { let name = Observable<String>("")
let address = Observable<String>("") lazy var nameAndAddress: EventProducer<(String, String)> = { return combineLatest(self.name, self.address) }() } let vm = MyViewModel() vm.nameAndAddress.observe { name, address in userNameLabel.text = name userAddressLabel.text = address } IUUQSYNBSCMFTDPNͱ͔ࢀߟʹͳΔΑ OBNFͱBEESFTT͕ λϓϧͰͬͯ͘Δ
ಜෆߦಧͳ 7JFX.PEFM Χϧςᶇ
class MyView { var viewType: ViewType func setup(viewType: ViewType) {
self.viewType = viewType switch viewType { case .Title: viewModel.title.observe { ... } case .Image: viewModel.image.observe { ... } case .Detail: viewModel.detail.observe { ... } } } } ಜෆߦಧͳ7JFX.PEFM ঢ় 7JFXଆʹঢ়ଶ݅ذ͕͋Δ
ಜෆߦಧͳ7JFX.PEFM පࠜ ঢ়ଶ݅அͷίʔυΛ 7JFX.PEFMʹҠ͍ͯ͠ͳ͍
ͦͦ7JFX.PEFM.PEFMͷӨͳͷͰ͢ɻ ͦͯ͠·ͨ7JFX7JFX.PEFMͷӨͰ͋Γ·͢ɻ Ҿ༻.77.ͷ.PEFMʹ·ͭΘΔޡղUIFTFBPGGFSUJMJUZ IUUQVHBZBIBUFCMPKQFOUSZNPEFMNJTUBLF
ಜෆߦಧͳ7JFX.PEFM ॲํᝦ ʮ7JFX7JFX.PEFMͷӨʯΛ పఈͤ͞Δ class MyView { func awakeFromNib() {
viewModel.title.observe { ... } viewModel.image.observe { ... } viewModel.detail.observe { ... } } } 7JFXԿஅΛߦΘͣɺ 7JFX.PEFMͷঢ়ଶΛ ʑͱ6*ʹөͤ͞Δ 7JFX.PEFMɺ 7JFXΛ࠶ߏͰ͖Δ͚ͩͷ ঢ়ଶใΛอ࣋͢Δ
ਆܦ࣭ͳ 7JFX.PEFM Χϧςᶈ
ਆܦ࣭ͳ7JFX.PEFM ঢ় 7JFX.PEFM͕ঢ়ଶΛࡉ͔࣋ͪ͗ͯ͘͢ΧΦε ɾμΠΞϩάͷදࣔඇදࣔঢ়ଶͱ͔ ɾը໘ભҠͷঢ়ଶͱ͔
ਆܦ࣭ͳ7JFX.PEFM පࠜ ʮঢ়ଶʯͱʮشൃੑͷݱʯΛ ۠ผͰ͖͍ͯͳ͍ ˞شൃੑͷݱඞͣফ͑Ώ͘ͷɻ
ਆܦ࣭ͳ7JFX.PEFM ॲํᝦ 0CTFSWBCMF7PJE class MyViewModel { let isUpdated = Observable<Void>()
private func didSomething() { isUpdated.next() } } 81' 8JOEPXT1SFTFOUBUJPO'PVOEBUJPO ʹ .FTTFOHFSͱ͍͏֓೦͕͋Δ 7JFX.PEFM͕Πϕϯτͷൃߦͱ͍͏ܗͰ7JFXʹ௨͢ΔΈ ղܾ͍ͨ͠ಉ͡ʜ ͩͱࢥ͏Μ͚ͩͲɺ 81'ͷݟ͕ͳ͍ͷͰ ؒҧͬͯͨΒ͢Έ·ͤΜʜ ͱΓ͋͑ͣ 0CTFSWBCMF7PJEศརͰ͢
ϝλϘϦοΫγϯυϩʔϜ 7JFX.PEFM Χϧςᶉ
ϝλϘͳ7JFX.PEFM ঢ় 7JFX.PEFM͕ ɹ ɹ௨৴ॲཧ ɹσʔλΩϟογϡ ɹ͞·͟·ͳۀͷϋϯυϦϯά ɹʜΛߦ͍ͬͯΔ
ϝλϘͳ7JFX.PEFM පࠜ .PEFMʹ͍ͭͯ ޡղͯ͠·ͤΜ͔
ϝλϘͳ7JFX.PEFM ॲํᝦ .PEFMΛ͔ͬ͠Γ࡞Ζ͏ .PEFM%"0 %BUB"DDFTT0CKFDU Ͱͳ͍ .PEFMۀϩδοΫ ඇ6*,JUͷผϓϥοτϑΥʔϜʹҠ২ͯ͠มΘΒͳ͍෦ Α͘7JFX$POUSPMMFSʹߦ͘Β͍ॻ͔ΕͯΔͭ ͋Ε΄ͱΜͲ.PEFMʹॻ͖͘ͷ
;ͨͨͼҾ༻"SDIJUFDUJOH"OESPJEʜ5IFDMFBOXBZ IUUQGFSOBOEPDFKBTDPNBSDIJUFDUJOHBOESPJEUIFDMFBOXBZ .77. ΞʔΩςΫνϟશମΛΧόʔͰ͖Δ֓೦Ͱͳ͘ ද෦ͷ࣮ʹա͗ͳ͍
.PEFM 7JFX.PEFM 7JFX %BUB #JOEJOH $PNNBOET ͜ͷ࿈ܞʹ͍ͭͯ.77.Կنఆͯ͠ͳ͍ ʢ.7˓શ෦ͦ͏͚ͩͲʣ
ͦ͏͍͏ͱ͜Λҙࣝͭͭ͠ɺ ΞϓϦશମʹΛͬͯ ݈߁తͳઃܭΛ͠ɺ ݈߁తͳίʔυΛॻ͖ɺ ݈߁తͳΤϯδχΞϥΠϑΛૹΓ·͠ΐ͏ʂ
'J/$Ͱ ݈߁తͳΤϯδχΞؒΛืू͍ͯ͠·͢