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
健康的なMVVM 書いてますか? ~MVVMアンチパターン集~
Search
takasek
April 27, 2016
Programming
26
20k
健康的なMVVM 書いてますか? ~MVVMアンチパターン集~
Health Swift Meetup (
http://finc-swift.connpass.com/event/29901/
) の発表資料です。
takasek
April 27, 2016
Tweet
Share
More Decks by takasek
See All by takasek
影響スケッチでFatViewControllerを可視化してみよう' / 20200530 effect sketch #swiftzoomin
takasek
1
1.3k
Clean Architecture: クライアントアプリの「中心」とは何か / 20200121 the center of the client #ios_ca
takasek
10
3.5k
SOLID原則を生活に適用する / 20190906 iOSDC
takasek
4
12k
APIレスポンスにおける直和型の表現を考える / 20190730 sekkeikaigi
takasek
4
1.4k
Dark Mode / 20190617 #wwdc_rusuban
takasek
11
1.3k
継続渡しと契約による設計 / 20190319 #tryswift_pre
takasek
8
2k
Continuation-Passing Style and Design By Contract(English ver.) / 20190319(E) #tryswift_pre
takasek
1
280
FiNCのクライアントアーキテクチャを揃える試み / 20190110 #app_mp
takasek
1
6.8k
SOLID原則のSとDとテストの話 - 「Swiftらしく設計する」Another / 20181221 #roppongiswift
takasek
1
1.2k
Other Decks in Programming
See All in Programming
とにかくAWS GameDay!AWSは世界の共通言語! / Anyway, AWS GameDay! AWS is the world's lingua franca!
seike460
PRO
1
550
Boost Performance and Developer Productivity with Jakarta EE 11
ivargrimstad
0
850
生成 AI を活用した toitta 切片分類機能の裏側 / Inside toitta's AI-Based Factoid Clustering
pokutuna
0
580
Hotwire or React? ~Reactの録画機能をHotwireに置き換えて得られた知見~ / hotwire_or_react
harunatsujita
9
4.1k
PHP でアセンブリ言語のように書く技術
memory1994
PRO
1
150
cXML という電子商取引の トランザクションを支える プロトコルと向きあっている話
phigasui
3
2.3k
Kotlin2でdataクラスの copyメソッドを禁止する/Data class copy function to have the same visibility as constructor
eichisanden
1
140
Googleのテストサイズを活用したテスト環境の構築
toms74209200
0
270
Sidekiqで実現する 長時間非同期処理の中断と再開 / Pausing and Resuming Long-Running Asynchronous Jobs with Sidekiq
hypermkt
6
2.7k
OpenTelemetryでRailsのパフォーマンス分析を始めてみよう(KoR2024)
ymtdzzz
4
1.6k
約9000個の自動テストの 時間を50分->10分に短縮 Flakyテストを1%以下に抑えた話
hatsu38
23
11k
Vitest Browser Mode への期待 / Vitest Browser Mode
odanado
PRO
2
1.7k
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
33
2.9k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.2k
Music & Morning Musume
bryan
46
6.1k
Intergalactic Javascript Robots from Outer Space
tanoku
268
27k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Writing Fast Ruby
sferik
626
61k
GitHub's CSS Performance
jonrohan
1030
460k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
664
120k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
14
1.9k
Building Your Own Lightsaber
phodgson
102
6.1k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
107
49k
The Cult of Friendly URLs
andyhume
78
6k
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/$Ͱ ݈߁తͳΤϯδχΞؒΛืू͍ͯ͠·͢