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

iOS Design Patterns: MVC vs. MVVM

iOS Design Patterns: MVC vs. MVVM

Introduction to the MVVM design pattern in iOS.
Presented at iOSOHO Women in iOS Meet Up in New York, NY Oct 19, 2016

Justine Kay

October 19, 2016
Tweet

Other Decks in Programming

Transcript

  1. MVC Model View Controller Where data is stored Where user

    interaction takes place Where notifications of user interaction and changes in the model are acted upon AND managed
  2. MVVM Model “View” View Model Where data is stored Where

    user interaction occurs Where user interactions and changes in the model are managed View View Controller Where notifications of user interaction and model changes are acted upon
  3. VIEW MODEL ‣ Complete value transformations ‣ Update the model

    as a result of user input ‣ Receive notifications about model changes ‣ Kick off asynchronous events ‣ Keep a view state to be presented
  4. VIEW CONTROLLER ‣ View lifecycle ‣ Animations ‣ Notifications about

    user input ‣ UI updates as a result of view model changes
  5. “MVVM IS NOT VERY GOOD” “MVVM IS EXCEPTIONALLY OK” “MVVM

    IS QUITE OK AT WHAT IT IS SUPPOSED TO DO” “MVVM TO THE RESCUE” “IT MAKES WRITING IOS APPS A JOY”
  6. Praises ‣ Easier to test ‣ Easier to extract reusable

    components ‣ Easier to reason about, extend and maintain
  7. var prezto: Prezto init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?, prezto:

    Prezto) { self.prezto = prezto super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } PresentingViewController - MVC
  8. var viewModel: PresentingViewModel init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?, prezto:

    Prezto) { self.viewModel = PresentingViewModel(prezto) super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } PresentingViewController - MVVM
  9. private func setScreenImages() { guard let screens = prezto.screens where

    screens.count > 0 else { return } currentScreenImageView.image = screens[prezto.currentIndex] if prezto.currentIndex == screens.count - 1 { nextScreenImageView.image = nil } else { nextScreenImageView.image = screens[prezto.currentIndex + 1] } previousScreenButton.enabled = prezto.currentIndex > 0 } PresentingViewController - MVC
  10. PresentingViewController - MVVM private func setScreenImages() { currentScreenImageView.image = viewModel.currentPreztoScreen

    nextScreenImageView.image = viewModel.nextPreztoScreen previousScreenButton.enabled = viewModel.previousScreenButtonEnabled }
  11. PresentingViewModel var currentPreztoScreen: UIImage? { guard let screens = prezto.screens

    where screens.count > 0 else {return nil} return screens[prezto.currentIndex] } var nextPreztoScreen: UIImage? { guard let screens = prezto.screens where screens.count > 0 && prezto.currentIndex != screens.count - 1 else {return nil} return screens[prezto.currentIndex + 1] } var previousScreenButtonEnabled: Bool { return prezto.currentIndex > 0 }
  12. @IBAction func previousScreenButtonTapped(sender: UIButton) { if prezto.currentIndex > 0 {

    prezto.currentIndex -= 1 updatePreztoCurrentIndex(prezto.currentIndex) } } @IBAction func nextScreenButtonTapped(sender: UIButton) { guard let screens = prezto.screens where screens.count > 1 else { return } if prezto.currentIndex < screens.count - 1 { prezto.currentIndex += 1 updatePreztoCurrentIndex(prezto.currentIndex) } else { showEndPresentationAlertView() } } PresentingViewController - MVC
  13. PresentingViewModel var preztoScreensCount: Int { return prezto.screens?.count ?? 0 }

    func previousScreenIndex() -> Int { if prezto.currentIndex > 0 { prezto.currentIndex -= 1 return prezto.currentIndex } return prezto.currentIndex } func nextScreenIndex() -> Int { if prezto.currentIndex == preztoScreensCount - 1 { return prezto.currentIndex + 1 } else { prezto.currentIndex += 1 return prezto.currentIndex } }
  14. private func updatePreztoCurrentIndex(index: Int) { prezto.currentIndex = index RequestManager.sharedInstance.updatePreztoCurrentIndex(prezto, completion:

    { result in switch result { case .Success(_): dispatch_async(dispatch_get_main_queue(), { self.setScreenImages() }) case .Failure(_): print("Failed to update Prezto's currentIndex") } } ) } PresentingViewController - MVC
  15. func updatePreztoCurrentIndex(index: Int, completion: () -> ()) { prezto.currentIndex =

    index RequestManager.sharedInstance.updatePreztoCurrentIndex(prezto, completion: { result in switch result { case .Success(_): dispatch_async(dispatch_get_main_queue(), { completion() }) case .Failure(_): print("Failed to update Prezto's currentIndex") } } ) } PresentingViewModel
  16. PresentingViewController - MVVM @IBAction func previousScreenButtonTapped(sender: UIButton) { viewModel.updatePreztoCurrentIndex(viewModel.previousScreenIndex()) {

    setScreenImages() } } @IBAction func nextScreenButtonTapped(sender: UIButton) { let nextScreenIndex = viewModel.nextScreenIndex() if nextScreenIndex == viewModel.preztoScreensCount { showEndPresentationAlertView() } else { presentingViewModel.updatePreztoCurrentIndex(nextScreenIndex) { setScreenImages() } } }
  17. Learning to program is a journey with no end. I

    believe that for the iOS community, MVVM is one stop along that journey, a layover to even better places… We can’t stop at MVVM, we have to keep moving. - Ash Furrow
  18. Thank you! Justine Kay iOS Engineer @ Intrepid Pursuits NYC

    Medium: https://medium.com/@JustineBethKay Twitter: @JustineBethKay Email: [email protected] Are you Intrepid? Intrepid.io