Introduction

In the following tutorial, we will build a sample iPhone app that initialises its Model and its View with Swinject, a Dependency Injection framework for Swift.

Swinject is a new, lightweight Dependency Injection (DI) framework for Swift, and this matters because:

  • there was only one DI tool available before Swinject: Typhoon; and
  • being older, Typhoon is larger, and more suited to Objective-C projects; but mainly because;
  • DI is an excellent way of safely defining scope within an application.

Put simply "dependency injection means giving an object its instance variables," as James Shore said in 2006. For more information on why this matters, and the importance of scope, try this Wikipedia article. Assuming you’re good with the concept of DI, let’s press on.

Protocols

With Swift 2.0, we are encouraged to use Protocol-Oriented Programming. Protocols are similar to Interfaces, which object-oriented programmers should be familar with. Essentially, a Swift protocol lists of functions that a given class should contain, or implement.

Here, a protocol called APIProtocol describes one method, call( URL ), which requires two String parameters: URL; and httpMethod, which any class which implements it must therefore have. For instance, both of the following classes implement APIProtocol, but similarly both would throw compile-time errors:

The former does not implement call( URL ) at all, whilst the latter declares httpMethod as an optional let, neither of which match the declaration in APIProtocol. The following is what we need.

Registering Protocols

Because it suits both DI and Swift, Swinject is built around the concept of protocols. It creates and injects instance variables by registering the abstract protocols that describe them - like APIProtocol above - with concrete instances that implement them - like APIService. Here's what that means in practice.

Container() is a class provided by Swinject, which handles DI registrations. Here, we are instantiating an instance of it, and then registering APIProtocol with it, with the instruction that when it is asked to create a new instance of APIProtocol, it will actually create an instance of APIService. Thereafter, we will instruct Swinject to create one or more instances of APIService for use in other classes, classes that presumably need to query the API.

That's the theory, now lets do some actual injecting!

Adding Swinject to your Project

Swinject itself first needs adding to your project via Cocoapods or Carthage.

Initialising Swinject

Swinject will take care of its own initialisation as long as you add the right class in the right way. In your project, create an empty Swift file called SwinjectStoryboard.swift and add the following code:

By extending Swinject's own SwinjectStoryboard class, and implementing the class function setup(), you will force Swinject into initialising your injectable properties for you. Next add the following:

The let container = Container() line under the header Registering Protocols we looked at above isn't strictly necessary. Swinject will have created a default Container for you, with the name defaultContainer, which I find a little too long, so shorten to swinject in order to avoid cluttering up my screen.

Registering Classes

Once we have access to this default container, we can use it to initialise our injectable properties:

Here we are referencing the APIService which implements the APIProtocol, which we mentioned earlier, and registering it with Swinject in the way we described earlier.

Registering Single Class Instances

With the above registration of APIService, every time an instance of APIService is injected, it will be a new, unique instance of it. But there is no need to have multiple gateways to our API, one will suffice, and Swinject allows the creation of singleton (with-a-small-s) instances with the simple addition of one line:

Singletons (with-a-capital-S) are controversial because of their global nature, and therefore best avoided for very good reasons. However, this implementation is singleton (with-a-small-s), which is not global, and therefore safe. What we are essentially telling Swinject is this:

  1. Don't make any instances of APIService;
  2. however, if any class asks for one, create a single instance of it, keep it in memory, and pass a reference to it to that class; and finally
  3. pass the same reference to any subsequent class that asks for its own instance of APIService.

If you are unsure about the difference between Singletons and singletons, and why the uppercase type should be avoided, I would strongly recommend you research them both.

Injecting Registered Class Instances

Here we introduce a second service, ImageService. As its name suggests, it is responsible for loading and serving images to its app. As part of its job, it needs to query the API, and so needs access to the single instance of APIService we registered immediately above. Swinject makes this simple, this time we give the property created in the register() method - which previously had the nameless underscore character - the name responder, and then use it to inject our single instance of APIService into a property within ImageService's init() method called api via responder's resolve() method.

Accepting Injected Registered Class Instances

ImageService, then, will need a way of accepting the api property it is passed, and this is how:

Thus, Swinject does the hard work of ensuring for us that only one instance of APIService is ever made, and that the self same instance is injected into any class instance that asks for it. For instance, we could now go on to add the following code:

An instance of LogService that now gets created will have an instance of APIService injected into it at initialisation, and crucially the same instance as that which will be injected into a new instance of ImageService. And as both LogService and ImageService utilise the line .inObjectScope( .Container ), they too will only be created as single instances, stored within Swinject.

Injecting Registered Class Instances into ViewControllers

Thus far we have only injected properties into Model type classes, but of course Swift apps are full of ViewControllers, which conceivably also need access to the API and the Logger and so forth. Naturally, Swinject has a method for this:

Swinject's registerForStoryboard() method accepts a ViewController prototype as its single argument, and then allows any formerly-registered classes to be injected into instances of it. However, ViewControllers accept Swinject injections via public properties and not init() methods. Here is what YourViewController.swift will have to look like:

Th implementation for ViewControllers is necessarily more complicated, involving computed properties, the write-once pattern, willSet, guard, etc. If you are not familiar with any of these techniques, I suggest you read up on them. Your-favoured-search-engine - whichever one it is - will help, I'm sure. The rationale behind using them is to ensure that each ViewController can have its injected properties injected just once, and that after having been injected they are private, and thus securely inaccessible to other parts of your app.

Given that these injected properties will not be available immediately, you will have to wait until the viewDidLoad() method is called, before referencing them: