Dependency Injection on iOS — part 2/4

An article about architecture, tests and much more

Fernando del Rio
13 min readNov 26, 2018

Hello again. I started writing a series of articles about Dependency Injection and now it's time for the part 2. As a quick overview:

  • We discussed Loose coupling and Tight coupling
  • We introduced the concept of Dependency Injection (DI)
  • Talked about these DI benefits: Reusability, Maintainability, Scalability and Testability
  • Introduced Swinject, a framework to work with DI on iOS

And for the part 2, as promised, we will start to see some real code. We will create an app from scratch and then discuss its architecture.

Obs.: Please don't blame for the initial code yet. I want to show bad design decisions, then we can evolve from there :-)

Summary

1. Contact App
1.1. Crafting the view
1.2. Crafting the model
1.3. Crafting the controller
1.4. The MVC
1.5. The massive view controller
2. Reusing UI elements
2.1. The problems with inheritance
2.2. Composition to the rescue
2.3. Protocol Extensions
2.4. Child view controllers
3. Model responsibilities
3.1. From the Apple guideline perspective
3.2. From a traditional perspective
3.3. The model
3.4. MVC refactored
4. TL;DR

1. Contact App

Let's start with this simple and dumb app called Contact App. Take a look on the gif below:

Contact App

This app has only 1 screen and the purpose it's to show details of a Contact: Photo, Name and Description. The data from this contact is retrieved from a web service. Once the user opened the app, it should shows a loading screen to indicate we're waiting the network activity to complete. When all contact data is retrieved, we should hide the loading screen and display the data in the UI. We also have a heart button, the user can use to favorite the contact if he want to. This interaction is only local just for a matter of simplicity.

Pretty simple right? How bad the code for an app like that could be 👀? Let's just do a regular approach any new iOS developer would do in order to create an app like that. Let's try to create, following what it's believed to be a good implementation of the MVC architecture.

1.1. Crafting the view

We start by drawing our UI in the already provided storyboard file, we add an UIImageView (to hold the contact's photo). We add 2 UILabels (for the name and the description). Then we add an UIButton with a heart background image.

We still need something to indicate the network activity, so we add an UIView with some opacity on the whole screen and an UIActivityIndicatorView inside. We pretend to show/hide it when as needed. We add the proper constraints there and the result is something like that:

UI designed for the contact app

So apparently this handles what we need in the "View" layer, right 👀? Moving on.

1.2. Crafting the model

We know the data coming from the web service, will come in a JSON format like that:

Then we know we should probably parse this JSON into some Codable struct like:

This represents well what we're trying to do, so it should fill the "Model" layer, right 👀? So we have Model, View, it should be only missing the Controller to glue everything and make out perfect app come to life 🧨.

1.3. Crafting the controller

Well, let's do that. We will create a subclass of UIViewController, link some IBOutlets there, and put some logic. The result is something like that:

Huge piece of UIViewController, hugh

So what is going on there, exactly? Well we have connected our outlets to access the UI elements, that we defined in the storyboard and we know they should be available when viewDidLoad is called. There we start our logic:

  • We set the initial state for the UI elements (because we know there's some dumb data in the storyboard)
  • We show the loading view to the user (to indicate network activity)
  • We call a function we defined in the view controller, in order to make a GET request to retrieve the contact data, using URLSession dataTask
  • We use JSONDecoder to convert the result Data into the Contact struct we created.
  • With the photo URL in hand, we call another function we defined in the view controller to download the contact's photo
  • With all information in hand we finally set the Photo, Name and Description in the UI
  • Then we finally hide the loading view and show the favorite button, so the user can interact with the contact. We make sure the loading view will be hidden in case of success or failure of the requests.
  • To keep the button toggling between favorited and not favorited, we use a boolean flag we defined in the view controller, and link an IBaction from the storyboard to the class. Everytime the user touches up inside the button, we change the button's image, based on this flag.

Mission accomplished, we wrote an amazing Controller, along with View and Model, right? Well not exactly.

1.4. The MVC

This is the MVC pattern from the Apple's conception:

Apple's MVC

The MVC pattern, should provide some separation of concerns, by decoupling the data from it's presentation, but it's frequently criticized by the community. What's the problem?

Well, most of the problems aren't happening because you are using MVC. An architectural pattern isn't a framework. It doesn't constrains you on how exactly you should implement it. There's no one watching. This may leave the implementation open to a subjective interpretation. Look the app we just built, we created something we called a model, created a controller and also created something we can call view, so it may seem right at first glance.

People frequently try to implement an architectural pattern like MVC, wondering it would solve all of their problems. What problems? Well by decoupling business logic from it's presentation, we wish things like we already discussed in the first article: More reuse, easier maintenance, more test coverage, tests more useful, scalability.

When we don't see our problems solved, we may think: Well there should exist something better. And start a hunt for the architecture of the moment. We try MVVM, we try Viper, we try N other approaches even derivated approaches for these architectures. We keep endless discussions on how to structure things, where to put code on these architectures, when we should be focusing on the problems we want to solve in the first place.

So before we try any fancy architecture, let's try to understand the code we just wrote in the beginning of the article. How easy it's for us to do maintenance? It's easy? Well it's a small app with only 1 screen, maybe it's easy now. But how it will be in the future? Yes, let's discuss about reusability and scalability with the things we already have.

1.5. The massive view controller

First let us draw the MVC diagram again, considering the proportions of our layers:

Massive view controller diagram

By the diagram it's possible to see our first problem. And probably one of the most discussed topics in iOS architecure: Massive View Controllers.

It's also a joke on the community, the Apple's MVC pattern would mean massive view controller.

This happens because a lot of reasons, but the way we learn to develop for iOS kinda, forces us to put most of code on view controllers. We wrote code tied to a Contact screen. How can we reuse code from here in other places, if it's deeply coupled to this specific screen?

2. Reusing UI elements

Once your view controllers start to becoming huge monsters, the first approach on trying to reuse things usually is to create a base class, that should be inherited by all other view controllers. Is this a good approach?

2.1. The problems with inheritance

There's a lot of criticism around inheritance and object oriented programming, check out this famous quote:

What is wrong with creating a base class?

We usually start by adding few things we want to reuse and end by having a massive base view controller, which is a nightmare to maintain.

Also, there's no multiple inheritance, all view controllers will need to adhere to the class and nothing else or we won't have the reuse. But once you inherit, looking for specific parts you wanted, you may also get a lot of things you didn't want to.

For instance, let's take a look on our Contact app. What UI elements can we reuse?

  • Probably other screens can make use of this show/hide loading capability. It would be a nightmare add that loading view to all screens of the app. Even though we were creating the view in code, it's could become something repetitive and we wish to write only once.
  • That favorite button is something that is tied to a Contact, but I can imagine it being used on some other places in the future. For instance, we may have a table view that display a list of contacts. Each cell could have a favorite button. How many places could have the same logic? It's repetitive and it should be cool to reuse that.

Okay so what's the issue of placing both things in a base view controller? Probably it would be useful to have a loading capability there, maybe makes sense for any screen to have the capability of displaying a loading to indicate network activity or something like that. But it certainly doesn't make sense to have a favorite button everywhere. If we put both things in a base class we will start to create something hard to manage.

So there's an easy solution for this inheritance problem? Yes, we can use composition instead of inheritance.

Prefer composition over inheritance —Albert Einstein

2.2. Composition to the rescue

There's many approaches for composition we can try. First, I want to show an approach that is really awesome, but it's still not that used on iOS development with Swift.

Everyone should be used to define protocols with properties and methods, and probably know that a class, struct etc. can adopt many protocols. At first glance it does not seem useful for composition: Once you adopt a protocol, you must provide the implementation, or it won't work. So if you adopt a protocol in different places, you should provide different implementations: We don't benefit from reusability

But there's a feature present since Swift 2, that allows us to add a default implementation to protocol's methods: It's called Protocol Extensions.

2.3. Protocol Extensions

As it's an extension, we can't use that to add a stored property, but it's useful to introduce the default implementation of methods. Let's see an example of use with our Show/Hide loading capability:

Loadable protocol: Show / Hide loadings

Let's talk about the nomenclature first. According to Apple's design API guidelines:

Protocols that describe what something is should read as nouns (e.g. Collection).

Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting).

I don't want to spend much time discussing the best names to use here, but for now let's stick to Loadable to name this protocol, as it adds the capability to show/hide loadings.

This protocol extension refers only to UIViewControllers.We defined that because we want to be able to add the LoadingView as a subview. We add some constraints, to make sure it will looks nice on every screen that adopts this protocol. And that's it. Any view controller that adopts this protocols, automatically obtain this implementation and we don't need to rewrite any code. We also didn't need to inherit from any base class 🙌

What other approaches we can use? Maybe a piece of UI is too complex and we need to use a better approach to reuse. When a view controller become complex, we can think on breaking it in child view controllers.

2.4. Child view controllers

Another technique usually underestimated. It exists since iOS 5 and allow us to create an hierarchy view controllers. There's a bigger parent view controller that contains one or more child view controllers, that can handles their own logic.

When we bind two view controllers like that, they share the same events (viewDidLoad, viewDidAppear, …) without the need of doing inheritance. They also will have this implicit connection: a child can access his parent, and a parent can access his children without needing to add a property doing that connection.

So, if we take a look on what we did in the show/hide loading example, there we built a simple hierarchy using only UIViews, with subviews and superviews, but with child view controllers we can take the next step introducing this hierarchy with UIViewControllers.

Let's take a look how we can break our view, to reuse the favorite button's logic:

Favorite child view controller

We moved some of the logic present in the Contact view controller to this Favorite view controller, in order to reuse this component in other places we may need in the future. This also helps us to reduce the size and complexity of our massive view controller. Also, I ended up writing the view programatically, just to show we don't always need to write things in the interface builder.

When using the child view controller, we also want the integration happening as much smooth as possible. We can consider refactoring our view in the storyboard to use a vertical stack view. This should help us to create a more "plug and play" view controller, making easy to add that favorite view controller in the bottom of the stack view, for instance.
Let's take a look on the distribution of what we created across the layers of MVC:

Our layers after introducing protocol extensions and child view controllers

The controller layer is still huge, but we moved responsibilities from the Contact view controller allowing the reuse of the Loading capability and the Favorite part of the view controller. So we can consider this is good for: Reusability, Scalability and Maintainability.

Obs.: It's worth mentioning that, it would be possible (and also really cool) to create the show/hide loading example, using child view controllers, but I did use protocols extensions to show the possibilities to deal with composition on iOS.

3. Model responsibilities

It's important to discuss what are the responsibilities for each layer of the MVC. First, let's talk about the model.

3.1. From the Apple guideline perspective

According, to apple guidelines:

Model objects encapsulate the data specific to an application and define the logic and computation that manipulate and process that data. For example, a model object might represent a character in a game or a contact in an address book.

3.2. From a traditional perspective

For me the quote above doesn't make it clear what exactly we can consider as part of the model layer. I prefer this more generic definition:

The model is 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.

3.3. The model

So what exactly is the model? It shouldn't be where I put my data structure? According to the definition above, the model should be the best place to put not only your data structure, but also where your business rules should live.

The model is what your application is independent of its presentation. This is interesting, because we are really used to implement most of the business rules inside the controller. And because of decisions like that it became hard to reuse these business rules with other controllers.

Think about our app. What it does? It downloads contact information and displays in the user interface. The contact information data structure, certainly belongs to the model layer, but the business rules responsible to download the contact information are also part of what our application is.

If we had decided to move that network code to a separated class, we won't be repeating ourselves, because we could reuse this class in other places, and we would also be reducing the size of our massive view controller. It could be something like:

Generic approach to retrieve a Codable

So let's just use this generic class inside our view controller? Not yet. We need to specify the proper URL, we need to download the contact and then its photo. This is a business rule logic, we could be reusing. Let's create another class like that:

Now we can move most of the business rules out of our contact view controller.

3.4. MVC refactored

With these changes, the ContactProvider class could be now the model our controller would use, in order to retrieve data to display in the UI. There is still logic in the controller but they are more presentation logic and less business rules, which is cool. It should be easier to reuse presentation logic anywhere with similar UI and also easier to reuse business logic in any place, as it's not tied to UI at all.

Also, currently the contact provider retrieves data from the internet. Can it retrieve from other data sources? (Maybe not now as there's a tight coupling with the NetworkProvider, but maybe with some changes 👀)

Let's draw again our layers and see how it's looking like:

Our layers redesigned after moving logic from the controller to the model

Pretty cool right? We managed to have smaller classes with less responsibilities working together to achieve the same result.

Please take a time to check all the code in detail. The code for the app before and after the refactoring are available on Github in the following link:
https://github.com/fernandodelrio/dependency-injection-article
The relevant code are in the folders:
1. A basic MVC implementation
2. Architecture Refactoring

4. TL;DR

The second part of this article introduced a really simple app, called Contact app. This app has only 1 screen, that loads data from a web service and displays it in the user interface.

We started with a messy architecture, with almost no reuse capabilities, really bad scalability, and a huge and unmaintainable view controller. We already improved our code in many aspects without even trying any fancy architecture pattern by splitting our view controller logic into smaller pieces. We did that using the concept of protocol extensions and child view controllers.

Finally, we moved most of the business rules logic from our controller layer and placed into somewhere into our model layer, reducing the size and complexity of our view controllers and helping to reuse more.

You probably noticed we didn't talk about Testability yet. That's because I want first, to speak about the issues of UIViewController. Probably, the most important piece we need to deal, when talking about the view architecture on iOS. And this is the main topic for the part 3 of our articles.

Again, feel free to leave some feedbacks, I will try to improve the articles based on them. See you there.

Links to all parts:
Part 1: https://medium.com/@fernandodelrio/dependency-injection-on-ios-part-1-4-8847f302b3d9
Part 2: https://medium.com/@fernandodelrio/dependency-injection-on-ios-part-2-4-359fe6800e90
Part 3: https://medium.com/@fernandodelrio/dependency-injection-on-ios-part-3-4-e85fe7e20de6
Part 4: https://medium.com/@fernandodelrio/dependency-injection-on-ios-part-4-4-ce3723d819d

--

--