The Foundations of Unit Testing (2/3): Effectively applying the MVC pattern in Swift

João Pedro de Amorim
Academy@EldoradoCPS
9 min readSep 9, 2020
credits: xkcd

Well, the good old Model-View-Controller pattern. So popular, so widespread and yet SO misunderstood. First introduced in Smalltalk-79, is definitely a go to choice for many developers and companies as a “standard” to follow — including Apple. But, have you ever read the definition of this pattern? Do you really know what every component in it stands for or you just have inferred it from context? Let’s jump right into it. In this article, we’re going to apply it to later, in the next one, effectively test it!

From Wikipedia, the three components are defined as:

Model — The central component of the pattern. It is the application’s dynamic data structure, independent of the user interface. It directly manages the data, logic and rules of the application.

View — Any representation of information such as a chart, diagram or table. Multiple views of the same information are possible, such as a bar chart for management and a tabular view for accountants.

Controller — Accepts input and converts it to commands for the model or view.

source: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

From here, I would like to point out a big misunderstanding that happens in the iOS community as a whole. Read the model definition once again. It is the central component of the pattern. It is not a class that merely holds data. It should in fact, contain the business logic that is pertinent to its data. It is not just a single class, also. It can be composed by several service classes that apply the business logic to the application’s data.

Also, let’s set down this once and for all: The controller is not the star of the show. And as mentioned above, the model class (or set of classes) is not a data container. Welp, I sincerely don’t know when everything lost track and we all end up with a MassiveViewController and POJO models approach of the MVC pattern. But, yeah, here I’ll do my best effort to dismantle this notion for you, reader.

Now that you effectively know the true definition of the MVC pattern, let’s try to apply it (and later on, test it!) in Swift using the knowledge previously brought to the table in the previous article. Hence, we’ll be relying upon abstractions and using protocols for such. And due to the fact that we’re using protocols, let’s follow a Protocol Oriented Programming (POP) approach.

So, let’s get our hands dirty! Here’s the deal: we’ll try to model a simple application that represents a gumball machine — and not any machine, it will be the Gumball3000!

note: The full project developed for this article is available on this github link: https://github.com/JPedroAmorim/GumballMachine3000

credits: https://br.freepik.com/ for the icons

note: In this tutorial, I’ll not be covering the basics of Swift, for instance: how to set up constraints in the Interface Builder, how to start a project in Xcode and so forth.

Since we’re following a POP approach, let’s start defining our protocols. We have three of them:

So, let’s talk a bit about what’s going on

  • GumballModelProtocol stands for the abstraction of our model. In our model, we’ll encapsulate the core logic of our application. In our case, the model will be responsible for dealing with the withdraw of gumballs and all the logic that revolves around it. The protocol is solely defined by the gumballWithdraw method, where an optional Int is the return, because we’re dealing with an operation that may fail (e.g. image trying to withdraw 10 gumballs when you have only 5 left in stock).
  • GumballViewProtocol will be, guess what, the abstraction of our cute view (which you already had a glimpse of it). The only information that is available about it for the exterior world is that the view should present a successful operation or a failing operation through the presentSuccess and presentError methods (we’ll talk about the setViewController method in no time, hang in there). Notice that the concrete implementation of how the message is displayed doesn’t come up — for classes interacting with the view, it shouldn’t matter on how the view displays this message (if it is through a label, through an image, through an animation…) the only thing that really matters is the fact that a message was effectively displayed.
  • GumballViewControllerProtocol will finally, be the abstraction of our controller. Perceive that its main purpose is to be a “man in the middle” between the view and the model — it takes input from the view and transforms it to a format that the model can handle. For instance, let’s take a look on the moneyWasSubmitted method — through it, our view will inform the controller about the user’s input (as a String), the controller will handle this input and transform it to an Int, and then, it will use it as an parameter for the model’s gumballWithdraw method.

I know that this amount of information right now might be overwhelming, but bare with me, it’ll become very clear once you see the code behind it!

note: Even though the model is rather simple in this approach, it definitely could be a set of classes, as mentioned earlier. In order to not keep this tutorial long, I’m just presenting a model with only one class. Though, in my github project, I will also present an alternative version (and more complex in design patterns terms) where the model protocol act as a facade for a whole set of classes.

But, before jumping into the code itself, let’s talk about the setViewController method in the GumballViewProtocol and the setModel and setView methods in the GumballViewControllerProtocol — let’s talk about dependency injection.

note: If you’re familiar with this concept, I know this “set methods” sounds too Java-ish. But nonetheless, even though it’s a Swift-based tutorial, I wanted to maintain core OOP patterns with a rather agnostic approach to the technological environment you are (be it Swift, Java, Python, whatever). But a more idiomatic Swift approach would definitely declare this dependencies as variables in the protocol.

Well, the term “dependency injection” make scare a few through as an eerie, complex terminology used in software architecture. But, to be quite sincere with you, as James Shore once said, it is a 25-dollar term for a 5-cent concept. So, what does dependency injection stands for?

Dependency injection is giving an object its instance variables (usually classes — we call them dependencies). That’s it.

What do I mean by that for our context? Simple.

Instead of this:

credits: Me, a majestic drawer

We’ll go with this:

credits: Me, a majestic drawer

Through dependency injection and protocols, we are transforming that big continuous lego block (that usually is your ViewController class) into small, discrete, interchangeable lego blocks.

Nice, now that we’ve defined our protocols, we’re good to start developing our first concrete implementation. Let’s start with our Model.

Let’s see whats going on:

  1. Here, we have our main variable. Notice that its access modifier is set to private, because the only class that should be able to alter its value should be the GumballModel. Notice that its existence is not disclosed in the GumballModelProtocol — because it’s an implementation detail and it shouldn’t matter for the outside world.
  2. Here, we have the concrete implementation of our gumballWithdraw method. Its logic is pretty straight forward: it should fail (produce a nil output) if the given quantity is either negative or if the quantity that is about to be withdrawn is larger than the amount of gumballs that are in stock. If it succeeds, this method should alter the value of the gumballs variable and return the withdrawn quantity.

note: if you are implementing the classes in this article by yourself, don’t forget to add them to the tests module by ticking its box on the “Target Membership” field in the class identity inspector tab.

Now, let’s head in for the implementation of our view. The key aspect of the view’s implementation is that we’ll be doing it through a .xib file instead of throwing outlets in a storyboard. Also, the owner of this .xib file will be a class that subclasses UIView instead of UIViewController.

Here’s our .xib and its outlets. Also, notice that we’ve set the File’s owner attribute to the GumballView class, whose implementation can be seen down below:

There’s a LOT to talk about here, so here we go:

  1. First, we have the views IBOutlets. Notice that outlets are an implementation detail! Hence, the controller should not have knowledge about them. The ONLY thing that the controller knows is that the view can present a successful operation or an error (the two methods disclosed in the view’s protocol).
  2. Here, we have the view’s only dependency: the view controller. It has a private(set) access modifier, and it basically means that this variable can be seen from an outer scope (like from another class) but only the GumballView is able to actually set it.
  3. Here, we have some initialization methods. First, we have the required init?(coder: NSCoder) method, which is mandatory to implement. Another init method that we’ll implement (and the reason for it will be clear once we reach the code for the view controller) is override init(frame: CGRect). In it, we call a setup method that properly initializes the view from the .xib file.
  4. Here, we have the dependency injection of this class dependency — the view controller.
  5. Also, here we have the implementation for the methods stated in the view’s protocol. Notice that the implementations details for a successful operation or an error presentation (if it is through an image, through an animation, through a label…) is encapsulated within the class and should not be accessible from an outer scope.
  6. In the end, we have a method that forwards the user input to the view controller, whom will do the proper handling of it.
  • One cool thing that I’d like to point out: What would happen if we moved from an Interface Builder approach and remodeled our view using Apple’s SwiftUI framework? If this new approach also come to implement the protocol we defined for the view, the rest of the code (view controller, model) would suffer no collateral damage. The only thing they know about the view is that it implements a protocol — nothing else. Using the Interface Builder or SwiftUI is a detail of the implementation, and it should only matter to the view itself.

And, finally, let’s hop in for our view controller:

Once again, we have a lot to talk about:

  1. Here, we define our dependencies, just as we did in our view class. But, this time, we have two of them: both the view and the model.
  2. Here, we have lifecycle related methods — and we call setup methods while on them.
  3. Here, we have a very important concept — this setup methods are used to set a DEFAULT value for both of our dependencies. In our case, the default values for them would be the concrete implementations of our view and model class. Since the view controller acts as a middle man between the view and the model (and have them as dependencies), it is an ideal place to set default values for them. Also, here’s the reason why we implemented override init(frame: CGRect) in the view — so that the view controller can instantiate it as a default value for the its view dependency.
  4. As before, the dependency injection methods. Pay attention to them, they’ll be of great use while testing our view controller.
  5. Here, we have the concrete implementation of the moneyWasSubmitted method. As mentioned in the start of this article, this method is responsible for handling the input provided by the view and format it in a way that the model can handle it. It also does some error checking and properly alert the view if something goes wrong.

Keep reading, in the next article we’ll develop our tests for what we’ve implemented so far!

--

--