Apologies about the very long delay between posts. We’ve been getting ready for a new addition to the family so we’ve been very busy! In the mean time I’ve updated all of the 3rd party libraries that ZombieInjection depends on and brought it up to the latest version of Swift.
I still wanted to go into more detail about how dependency injection works in my ZombieInjection project. When I was first trying to wrap my brain around this concept, a full fledged example of seeing it in action would have really been beneficial a few years ago… I hope this is helpful for you!
Spoiler: Yet again, Swift’s generics system puts a damper on things 😔
What is Dependency Injection?
Let’s start with some definitions.
From what I can tell the first use of the term “Dependency Injection” is by Martin Fowler when he described this concept in his blog post, Inversion of Control Containers and the Dependency Injection Pattern way back in 2004, as:
The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface
Ok that definition doesn’t quite work for me. Moving on.
Mark Seemann, author of the excellent book Dependency Injection in .NET defines Dependency Injection as:
… a set of software design principles and patterns that enable us to develop loosely coupled code.
That’s a little better but kind of nebulous still… Moving on.
The simplest definition I could find comes from James Shore, thought leader in the Agile software development community:
Dependency injection means giving an object its instance variables. Really. That’s it.
Mind. Blown. Is that really all?
Let’s take a look at a super simple example. Below is an example of a simple struct that creates a private NonInjectedObject at initialization and is not making any use of dependency injection:
Now let’s take a look at the same example code except architected to use dependency injection:
You might be thinking… Uhhh OK, all you did was force the person calling the initializer to send in your object… Bingo!
Admittedly, this is a contrite example and doesn’t show you the full benefit of proper dependency injection. Let’s abstract our example class even more and use protocol oriented programming in conjunction with dependency injection:
Now that’s what I’m talking about! In the above example, you have now ended up with a decoupled and highly testable solution. You can easily mock the injected dependency for your unit tests so that you can easily abstract out complex functionality that is not currently part of the test you are trying to write. Loosely coupled code doesn’t just enhance your testability — it also increases the reusability and extendability of your code.
The example I used above used a type of dependency injection called Constructor Injection. Mark Seemann describes 4 types of dependency injection in Dependency Injection in .NET that you can make use of (Yes, I realize I keep referencing a book that mentions .NET in a blog post for Swift… Get over it! 😁)
Types of Dependency Injection
Constructor Injection – Requires all callers to supply the dependency as a parameter to the class’s constructor. See above example.
Property Injection – The consumer gets its dependency through a writable property that also has some default value. In iOS if you have used the delegate pattern, you have used this type of dependency injection.
Method Injection – Supplying the dependency as a method parameter. This would be used if your dependency can change for each method call like so:
Ambient Context – Makes your dependency available to every module without extra changes to your modules. This is typically done by creating a static property or method that could be called anywhere in your code. This should rarely ever be used. Where it might make sense is to use it on a cross-cutting concern like calling your logging framework so that you don’t have to inject your logging dependency into *EVERY* initializer which would pollute every API.
Now that you’ve seen the different kinds of dependency injection let’s talk about the concept of the composition root.
I first saw this idea of a composition root from Mark Seemann (See a theme? 😎) Mark describes it as:
A Composition Root is a (preferably) unique location in an application where modules are composed together.
But what does it mean?
What it means for iOS developers is that if you want to follow this kind of pattern, you should wire up all of your dependencies at startup in your AppDelegate. This way there is only one place in the code you need to look at to figure out your dependencies as opposed to having it spread out all over your project.
ZombieInjection’s Composition Root
In my project, the Composition Root is setup using the excellent Dip framework like so:
First, configure all of my dependencies in the DependencyContainer provided by Dip.
Second, call the configure method in my AppDelegate to actually do the configuration:
What this ended up meaning is that my main view model had to end up holding all of the dependencies and then sending them into lower view models via constructor injection. Here is what this looks like:
The flow goes like this… The ZombieListViewController is instantiated in the AppDelegate and then sets the dependencies on the ZombieListViewModel once the ZombieListViewController is done being created. The ZombieListViewModel then is holding references to these objects to send to lower screens, like ZombieDetailViewController.
For small applications this seems to work fine as there are not too many dependencies. You are then probably wondering… What about big applications!? Do I really want to carry all of these dependencies if I’m not sure if they’ll even be used?
In .NET, a solution could be to set up your dependency variables as lazily loaded so that they can be created only when they are needed, however that will not work here as I’ve implemented this solution using protocol oriented programming. Swift requires your variables to have initializers, i.e. you can’t put the keyword
lazy in front of a Protocol property or you will get a compiler error. So at this point, until the Swift generics system gets better, your only option is to create all of these dependencies up front and pass them through or don’t use a Composition Root.
Even with some of the limitations of Swift’s generics system, I still love using Dependency Injection as much as possible. It truly does end up in code that is much more loosely coupled and much more testable. For the time being, you could use something like the Service-Locator Pattern (Full disclosure many see this as an anti-pattern) or something else to get around this limitation. I plan on engaging the Swift Evolution thread to see if this is something that will happen later on and will update if I create or come across a specific proposal that covers this.