Upgrade to Pro — share decks privately, control downloads, hide ads and more …

健康的なMVVM 書いてますか? ~MVVMアンチパターン集~

健康的なMVVM 書いてますか? ~MVVMアンチパターン集~

Health Swift Meetup (http://finc-swift.connpass.com/event/29901/) の発表資料です。

704056da9a4c4e075ad14479beaebab7?s=128

takasek

April 27, 2016
Tweet

Transcript

  1. ݈߁తͳ.77. ॻ͍ͯ·͔͢ʁ d.77.Ξϯνύλʔϯूd )FBMUI4XJGU.FFUVQ CZ XJUI'J/$

  2. !UBLBTFL w GSFFMBODFJ04&OHJOFFS w 'J/$͞ΜͰ͓࢓ࣄ͍͍ͤͯͨͩͯ͞·͢ w ɹ!UBLBTFL w ɹUBLBTFL w

    ɹUBLBTFL
  3. !UBLBTFL w /PUJGXJGU
 IUUQTHJUIVCDPNUBLBTFL/PUJGXJGU
 /4/PUJpDBUJPOͷVTFS*OGPΛ
 4XJGUZʹѻ͏ϚΠΫϩϥΠϒϥϦ w "DUJPO$MPTVSBCMF
 IUUQTHJUIVCDPNUBLBTFL"DUJPO$MPTVSBCMF
 UBSHFUBDUJPOΛ౉͢ॲཧΛ


    4XJGUZͳΫϩʔδϟͰॻ͚Δ
 ϚΠΫϩϥΠϒϥϦ (JU)VCͰ
 044ϥΠϒϥϦ΋࡞ͬͯ·͢
  4. ͓͞Β͍ .77.ͱ͸

  5. .PEFM 7JFX.PEFM 7JFX %BUB #JOEJOH $PNNBOET ɹ%BUB#JOEJOHͷ࣮ݱͷͨΊʹ͸ɺJ04ͷ৔߹ ɹɹ'31ϥΠϒϥϦ 3Y4XJGU 3FBDUJWF$PDPB

    ΍ ɹɹσʔλόΠϯσΟϯάϥΠϒϥϦ 4XJGU#POE ͷ ɹɹαϙʔτ͕ඞཁ ɹ ˞'J/$Ͱ͸ݱࡏ4XJGU#POEΛར༻
  6. ຊ୊

  7. ͜Ε͔Β
 ෆ݈߁ͳ7JFX.PEFMΛ
 ਍࡯͠·͢

  8. Ͱ͠Ό͹Γ7JFX.PEFM Χϧςᶃ


  9. 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΁ͷࢀরΛ࣋ͬͯɺ௚઀ૢ࡞͢Δ
  10. Ͱ͠Ό͹Γ7JFX.PEFM පࠜ 
 ࠜຊతʹઃܭ͕͓͔͍͠ w 7JFXʹमਖ਼͕ೖͬͨΒ7JFX.PEFM΋मਖ਼͠ͳ͖Όʜ w ͔ͤͬ͘ͷ.77.ύλʔϯ͕୆ແͩ͠ʂ w ʜͱ͍͏͔ɺ͜Ε.77.ʹͳͬͯͳ͍

  11. ґଘͷํ޲͸ 7JFXɹɹ7JFX.PEFM 7JFX.PEFMɹɹ.PEFM Ҿ༻"SDIJUFDUJOH"OESPJEʜ5IFDMFBOXBZ 
 IUUQGFSOBOEPDFKBTDPNBSDIJUFDUJOHBOESPJEUIFDMFBOXBZ 7JFX.PEFM 7JFX σʔλόΠϯσΟϯά

  12. Ͱ͠Ό͹Γ7JFX.PEFM ॲํᝦ w 7JFX.PEFM͸ࣗ෼ࣗ਎ͷঢ়ଶΛมߋ͢Δ͚ͩ w Ͳ͏࢖ΘΕΔ͔͸Ұ੾ؔ஌͠ͳ͍͠ɺ
 ୭ 7JFX ʹόΠϯυ͞Ε͍ͯΔ͔΋஌Βͳ͍
 ʹ7JFXʹґଘ͠ͳ͍


    ʹมߋʹڧ͍ 7JFX.PEFMˠ7JFX͸ ඞͣόΠϯσΟϯάͰܨ͙
  13. 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 վળྫ
  14. ࠞઢ͍ͯ͠Δ 7JFX.PEFM Χϧςᶄ


  15. ࠞઢ͍ͯ͠Δ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Λ ड͚औΔ
  16. ࠞઢ͍ͯ͠Δ7JFX.PEFM පࠜ
 ॲཧͷྲྀΕ͕ ੔ཧͰ͖͍ͯͳ͍

  17. ࠞઢ͍ͯ͠Δ7JFX.PEFM ॲํᝦ جຊɺ
 7JFXˠ7JFX.PEFM͸ίϚϯυΛୟ͚ͩ͘ 7JFX.PEFMˠ7JFX͸όΠϯυ͢Δ͚ͩ 7JFX.PEFM 7JFX %BUB #JOEJOH $PNNBOET

    ᶃίϚϯυ ᶄঢ়ଶมߋ ᶅঢ়ଶ൓ө †એݴతʹهड़
  18. Ψϥεͷ7JFX.PEFM Χϧςᶅ


  19. class MyViewModel { let text = Observable<String>("") let textLength =

    Observable<Int>(0) } vm.text.value = "ͳΜ͔௕͍ςΩετ" vm.text // "ͳΜ͔௕͍ςΩετ" vm.textLength // 0 ←!!?!!? Ψϥεͷ7JFX.PEFM ঱ঢ় յΕͦ͏ͳ0CTFSWBCMF͹͔ΓूΊͯ͠·͏
  20. Ψϥεͷ7JFX.PEFM පࠜ
 ঢ়ଶͷओैؔ܎Λ
 એݴతʹදݱͰ͖͍ͯͳ͍ w UFYUΛมߋͨ͠Βɺ
 ͦͷϝιουͰUFYU-FOHUI΋
 มߋ͢ΔΑ͏ʹؾΛ͚ͭΔʁ
 ͍΍͍΍ɺͦΜͳͷਓ͕ؒ
 έΞ͢΂͖͜ͱ͡Όͳ͍͔Βʂ

    ຖճҪށ͔Β ਫΛ἞·͞ΕͯΔΑ͏ͳ ॏ࿑ಇײ
  21. Ψϥεͷ7JFX.PEFM ॲํᝦ $PMEͳ0CTFSWBCMFΛ࢖͏ )PU
 0CTFSWBCMF $PME
 &WFOU1SPEVDFS

  22. 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-FOHUI͸NBQ USBOTGPSNJOHPQFSBUPS Λ௨͚ͩ͢ͷ ɹ$PMEͳଘࡏʹ͠ɺ੔߹ੑΛอূ એݴతʹهड़Ͱ͖ͨʂ
  23. ઉ଍Βͣͳ
 7JFX.PEFM Χϧςᶆ


  24. 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 ͲͪΒ͕มߋ͞Εͯ΋ ྆ํΛ࢖ͬͯߋ৽ॲཧΛ ߦ͍͍ͨʂ
  25. ઉ଍Βͣͳ7JFX.PEFM පࠜ σʔλΛద੾ͳཻ౓ɾ૊Ͱ ౉͍ͯ͠ͳ͍

  26. ઉ଍Βͣͳ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͕ λϓϧͰ౉ͬͯ͘Δ
  27. ؂ಜෆߦಧͳ
 7JFX.PEFM Χϧςᶇ


  28. 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ଆʹঢ়ଶ΍৚݅෼ذ͕͋Δ
  29. ؂ಜෆߦಧͳ7JFX.PEFM පࠜ
 ঢ়ଶ΍৚݅൑அͷίʔυΛ
 7JFX.PEFMʹҠ͍ͯ͠ͳ͍

  30. ͦ΋ͦ΋7JFX.PEFM͸.PEFMͷӨͳͷͰ͢ɻ
 ͦͯ͠·ͨ7JFX͸7JFX.PEFMͷӨͰ΋͋Γ·͢ɻ Ҿ༻.77.ͷ.PEFMʹ·ͭΘΔޡղUIFTFBPGGFSUJMJUZ IUUQVHBZBIBUFCMPKQFOUSZNPEFMNJTUBLF

  31. ؂ಜෆߦಧͳ7JFX.PEFM ॲํᝦ ʮ7JFX͸7JFX.PEFMͷӨʯΛ పఈͤ͞Δ class MyView { func awakeFromNib() {

    viewModel.title.observe { ... } viewModel.image.observe { ... } viewModel.detail.observe { ... } } } 7JFX͸Կ΋൑அΛߦΘͣɺ 7JFX.PEFMͷঢ়ଶΛ ໧ʑͱ6*ʹ൓өͤ͞Δ 7JFX.PEFM͸ɺ 7JFXΛ࠶ߏ੒Ͱ͖Δ͚ͩͷ ঢ়ଶ৘ใΛอ࣋͢Δ
  32. ਆܦ࣭ͳ
 7JFX.PEFM Χϧςᶈ


  33. ਆܦ࣭ͳ7JFX.PEFM ঱ঢ় 7JFX.PEFM͕ঢ়ଶΛࡉ͔࣋ͪ͗ͯ͘͢ΧΦε ɾμΠΞϩάͷදࣔඇදࣔঢ়ଶͱ͔ ɾը໘ભҠͷঢ়ଶͱ͔

  34. ਆܦ࣭ͳ7JFX.PEFM පࠜ ʮঢ়ଶʯͱʮشൃੑͷݱ৅ʯΛ ۠ผͰ͖͍ͯͳ͍ ˞شൃੑͷݱ৅ඞͣফ͑Ώ͘΋ͷɻ

  35. ਆܦ࣭ͳ7JFX.PEFM ॲํᝦ 0CTFSWBCMF7PJE class MyViewModel { let isUpdated = Observable<Void>()

    private func didSomething() { isUpdated.next() } } 81' 8JOEPXT1SFTFOUBUJPO'PVOEBUJPO ʹ͸ .FTTFOHFSͱ͍͏֓೦͕͋Δ
 7JFX.PEFM͕Πϕϯτͷൃߦͱ͍͏ܗͰ7JFXʹ௨஌͢Δ࢓૊Έ ղܾ͍ͨ͠໰୊͸ಉ͡ʜ ͩͱࢥ͏Μ͚ͩͲɺ 81'ͷ஌ݟ͕ͳ͍ͷͰ ؒҧͬͯͨΒ͢Έ·ͤΜʜ ͱΓ͋͑ͣ 0CTFSWBCMF7PJE͸ศརͰ͢
  36. ϝλϘϦοΫγϯυϩʔϜ
 7JFX.PEFM Χϧςᶉ


  37. ϝλϘͳ7JFX.PEFM ঱ঢ় 7JFX.PEFM͕
 ɹ ɹ௨৴ॲཧ
 ɹσʔλΩϟογϡ
 ɹ͞·͟·ͳۀ຿ͷϋϯυϦϯά 
 ɹʜΛߦ͍ͬͯΔ

  38. ϝλϘͳ7JFX.PEFM පࠜ .PEFMʹ͍ͭͯ ޡղͯ͠·ͤΜ͔

  39. ϝλϘͳ7JFX.PEFM ॲํᝦ .PEFM૚Λ͔ͬ͠Γ࡞Ζ͏ .PEFM%"0 %BUB"DDFTT0CKFDU Ͱ͸ͳ͍ .PEFMۀ຿ϩδοΫ ඇ6*,JUͷผϓϥοτϑΥʔϜʹҠ২ͯ͠΋มΘΒͳ͍෦෼ Α͘7JFX$POUSPMMFSʹߦ͘Β͍ॻ͔ΕͯΔ΍ͭ ͋Ε͸΄ͱΜͲ.PEFMʹॻ͘΂͖΋ͷ

  40. ;ͨͨͼҾ༻"SDIJUFDUJOH"OESPJEʜ5IFDMFBOXBZ 
 IUUQGFSOBOEPDFKBTDPNBSDIJUFDUJOHBOESPJEUIFDMFBOXBZ .77.͸ ΞʔΩςΫνϟશମΛΧόʔͰ͖Δ֓೦Ͱ͸ͳ͘ ද૚෦෼ͷ࣮૷ʹա͗ͳ͍

  41. .PEFM 7JFX.PEFM 7JFX %BUB #JOEJOH $PNNBOET ͜ͷ࿈ܞʹ͍ͭͯ͸.77.͸Կ΋نఆͯ͠ͳ͍ ʢ.7˓શ෦ͦ͏͚ͩͲʣ

  42. ͦ͏͍͏ͱ͜Λҙࣝͭͭ͠ɺ ΞϓϦશମʹ໨Λ഑ͬͯ ݈߁తͳઃܭΛ͠ɺ ݈߁తͳίʔυΛॻ͖ɺ ݈߁తͳΤϯδχΞϥΠϑΛૹΓ·͠ΐ͏ʂ

  43. 'J/$Ͱ͸ ݈߁తͳΤϯδχΞ஥ؒΛืू͍ͯ͠·͢