The missing piece of MVVM

Terrick Mansur
4 min readDec 10, 2019

Model-View-viewmodel (MVVM) is one of the most used, and known patterns in mobile development. At its core, MVVM helps you separate your view logic from your business logic. The idea is that your views should not know where or how the data it is displaying came to be. In this article I want to show you how I write MVVM, and why I write it the way I do. I have seen many code bases that can be improved with what I am about to write. So let’s get into it.

When I am writing a pattern (or any code for that matter), the first thing I think about is; What do I want to accomplish with these patterns?. This is already known with MVVM. In essence what you want to accomplish with MVVM is the following;

  • Separate business logic from view logic.

They will tell you that this will make your code more testable (which is not necessarily true, but this is a topic for another day).

So, what is the best way to do this? Since I am an iOS developer, I will be using UIKit and Swift code examples.

In this article I will be building the very simple screen (image below).

This screen has two labels. One is the title (my name) and the other is a date. In a lot of code bases you will see that the MVVM pattern consists of two pieces. The viewModel and the UIViewController. The code will look very much like the code below.

The view controller file:

class SomeViewController: UIViewController {    // MARK: - IBOutlets
@IBOutlet
private var titleLabel: UILabel!
@IBOutlet private var dateLabel: UILabel!
var viewModel = SomeViewModel() override func viewDidLoad() {
super.viewDidLoad()
updateViews()
}
private func updateViews() {
self.titleLabel.text = self.viewModel.title
self.dateLabel.text = self.viewModel.date
}
}

The view model file:

import Foundationclass SomeViewModel {
let title = "Terrick Mansur"
let date = "September 19 2019"
}

Does this work? Yes, but I would argue there is a better way to write your MVVM and that there is a third piece missing in this setup.

So let’s add a function to the viewModel that updates its title and date attributes.

func update(title: String, date: String) {
self.title = title
self.date = date
didUpdate()
}

This function will update the attributes in the viewModel (yes, we have to change the title and date string from lets to vars). Here is where I would argue that the cracks of this setup starts to show. What is wrong with this setup?

Let’s revisit what we are trying to accomplish with MVVM. “The view controller (VC) should not know how the data comes to be“. For this setup you can still argue that the VC does not know where the data is coming from, but why does the VC have access to the VM update function if it should never know where the data is coming from. Simply put, if the VC ever calls the update function of the VM, then you are doing something wrong. And if this is true, why would you even allow the VC to have access to this function? It has no rights changing the state of the VM; the view should only reflect the state of the VM.

This is where I will introduce the third piece of the MVVM setup, the viewModelType protocol.

SomeViewModelType protocol:

protocol SomeViewModelProtocol {
var title: String { get }
var date: String { get }
}

Use this type when you are declaring your viewModel in the VC like this;

let viewModel: SomeViewModelProtocol = SomeViewModel()

And have the VM implement it like this;

class SomeViewModel: SomeViewModelProtocol {    // MARK: - SomeViewModelProtocol
var
title = "Terrick "
var date = "September 19 2019"
...}

With this setup, your VC can no longer call update on your viewModel, you have full control over what your VC can see, and do, with regards to the VM.

There are additional advantages with you will gain be introducing the viewModelType to your setup.

  • You can more easily swap viewModel implementation for the given VC.
  • You can more easily test your VC be implementing a testableVM
  • You can write you entire VC without even defining your viewModel. This gives the person writing the VC a clearer thought process when writing the VC.

The bigger picture here is that it is always important to use the features of your programming to enforce patterns you are trying to follow. When your code is following a pattern, yes everyone should understand it and follow it. But it is also very important to design your code in such a way to enforce it. This will help new people coming into the team to understand the patterns and minimise mistakes.

--

--