A few months ago, I had an interesting conversation with a reader about the Model-View-ViewModel pattern. Let's call him John. The questions John asked me show that developers new to MVVM often miss or overlook the more profound benefits the pattern brings to the table. In this episode, I'd like to share with you the most important insights of my conversation with John.

Creating a View Model

I believe John read my book, Learn the Four Swift Patterns I Swear By, and came across the chapter in which I discuss the MVVM pattern. The first question he had for me was the most obvious one. He shared with me the implementation of a view controller that looked something like this. For confidentiality reasons, I won't be sharing John's implementation.

import UIKit

class ViewController: UIViewController {

    // MARK: - Properties

    private var notes: [Note] = []

    // MARK: -

    private lazy var apiManager = APIManager()

    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()

        // Fetch Notes
        apiManager.fetchNotes { [unowned self] (notes, error) in
            if error != nil {
                // Error Handling
                ...

            } else {
                // Update Notes
                self.notes = notes

                // Update View
                self.updateView()
            }
        }
    }

    // MARK: - View Methods

    private func setupView() { ... }

    private func updateView() { ... }

}

This may look familiar if you've been using the Model-View-Controller pattern to build Swift applications. The moment the view controller has finished loading its view, it performs a network request. It delegates this task to the APIManager class, which is a good start to reduce the responsibilities of the view controller.

John then showed me how we planned to implement the MVVM pattern. This is what that looked liked. I've kept the example as simple as possible.

import UIKit

class ViewController: UIViewController {

    // MARK: - Properties

    private var viewModel: ViewModel!

    // MARK: - View Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()

        // Fetch Notes
        viewModel.fetchNotes { [unowned self] (notes, error) in
            if error != nil {
                // Error Handling
                ...

            } else {
                // Update View
                self.updateView(with: notes)
            }
        }
    }

    // MARK: - View Methods

    private func setupView() { ... }

    private func updateView(with notes: [Note]) { ... }

}

After showing me the implementation, John asked me the most important question of the conversation. "I just add a view model? Is that necessary for such a simple view controller?" John understands what the Model-View-ViewModel pattern is and how it can be implemented, but his question implied that he missed the more profound benefits of the pattern. He saw the MVVM pattern as a slightly more sophisticated implementation of the MVC pattern, with a few additional bells and whistles.

Before I continue, it's time to put your brains to work. You've now seen both implementations. The MVVM implementation is simple and we could make the implementation a bit better. But that's not the point. Can you see how this simple view controller is benefiting from the MVVM pattern? Take a minute and think about the possible advantages MVVM has in this simple implementation.

Thinking Bigger

Most developers solve the problems they face without taking a look at the bigger picture. For many developers, the implementations I showed you in this episode look very similar and I assure you that a surprising number chooses the first implementation over the second one. Why is that?

The first implementation looks less complex and that may be true. It has a few flaws, though. Why is the view controller responsible for making a network request? It's true that the API manager helps the view controller with this task, but the view controller still knows that the notes it fetches come from a backend. You could argue that these details are hidden by the API manager, but that's not the point. The point is that the view controller controls a view and it shouldn't know where the notes come from. It should only need to ask for content to display in its view. It shouldn't even know about the API manager let alone how it does its job.

The view controller of the MVC implementation also manages state, an array of Note instances. This is typical for the MVC pattern. This isn't bad or wrong, but the MVVM pattern hides this detail in the view model.

What's in It for You

If we take a close look at the second implementation in which we use a very simple view model, we discover that the view controller is only responsible for controlling its view. It configures the view it manages and responds to user interaction. These tasks are the only responsibilities a view controller should have.

The view model may appear as just another object in the object graph. But it's more than that. It hands the view controller an array of notes. The view controller has no idea where the notes come from and it shouldn't be concerned with these details. They could be fetched from a backend, but they might as well come from a local cache managed by Core Data or Realm.

This also makes unit testing so much easier. Unit testing the view model is trivial and the testability of the view controller automatically improves.

Step Up Your Game

I strongly believe robust, testable code is built in a way that approaches the problem from a different perspective. We could stick with the MVC implementation, but I hope this episode has convinced you that it isn't the best solution. Using MVVM may look complex, but it really isn't. It simply requires you to think differently and bigger.

If you're new to Swift development, then it's fine to embrace the MVC pattern. I believe that's a good place to start because it keeps everything a bit simpler. However, I hope you don't stop there. I hope you have the ambition to evolve and grow as a developer.

As developers, it's very tempting to focus on the shiny stuff Apple throws at us every year, ARKit, Core ML, Metal, ... These are great technologies and it's a lot of fun exploring them. But focusing on your foundation as a developer should be your top priority. Always. That's my obsession as a developer and the reason for creating Cocoacasts. With a solid foundation, you can play with Apple's shiny stuff without having to worry about using them.

If you'd like to learn how to take your projects to the next level with the MVVM pattern, then you should check out Mastering MVVM With Swift. But remember that MVVM is nothing more than a tool to add structure to your projects. MVVM isn't the only solution to accomplish that. You might prefer MVP or VIPER. Remember that the best solution is the one that works for you and your team.