In the following tutorial, we will build a sample iPhone app that communicates internally via Signals, a Swift library for creating and observing events.

Introduction

Plumbing, architecture, boilerplate. Whatever name you choose, finding the best way of doing the boring stuff of moving stuff around your app safely and efficiently is a problem we all have to address. Signals by Tuomas Artman via Dependency Injection is my favoured approach. A Signal is an event that is broadcast and can be listened to by other classes, and reacted to if needs be.

Dependency Injection and MVC

The cleanest way to implement Signals is to inject a singleton-with-a-small-s Signals' event bus as a "service" using Dependency Injection. Don't fret if the preceding sentence was Greek to you, I have written a tutorial about Dependency Injection here, which you might want to read first, and I will explain the rest below.

In MVC - or Model-View-Controller - driven development, apps cleanly separate their user interfaces from the data they display, and the Model is the part of any given app that deals solely with data. MVC Models are chiefly constructed from two types of classes:

  1. proxies, that store data; and
  2. services, that transport data.

What we will do is to create a single instance of a class called SignalsService, and then inject it throughout our application.

Creating SignalsProtocol

If you've digested my Dependency Injection tutorial, you will know that the first thing we need to create is a protocol for our service. Create a new Swift file called SignalsProtocol.swift and add the following code:

This is a complete list of the basic functions needed to make the bus work. Let's implement them one-by-one.

Create a new Swift file called SignalsService.swift and add the following code:

This implements the four functions in the protocol. Instances of other classes will use these four methods to listen for and stop listening for signals, and to dispatch and delete signals. It imports Signals at the top, which is Tuomas Artman's library that you will need to add to your project via Cocoapods or Carthage. It also adds the private variables _signals and _signatures, which we will use later to store our signals.

Initialise these two variables immediately thus:

Next add the following private function:

This function checks whether a given listener already exists or not, and returns a Boolean. As you can see, it calls for a class called SignalSignature, so let's create it.

Create a new Swift file called SignalSignature.swift and add the following code:

Every time a new Signal is created, a unique signature is created with it. This ensures that signals are never duplicated. A SignalSignature object requires: a String key, which identifies a particular event, and might be something like API_CALL_RETURNED; and an AnyObject listener, which would be the class instance that wishes to listen for the API_CALL_RETURNED signal. Thus each SignalSignature ensures that each class instance can only listen once for any given type of signal.

Next add the following private function:

_getSignalBy( Key ) will return an existing Signal, or create one and then return it if it doesn't already exist and the parameter createIfMissing is true. Each and every signal is identified by a key - such as API_CALL_RETURNED mentioned just above - and can have multiple listeners listening to it.

Next add the following private function:

This function will be called by the public function addListenerFor( Key ). We could of course simply put the functionality for adding a listener in addListenerFor( Key ) itself, but should later we wish to have a function that adds multiple listeners, such as addListenersFor( Keys ), it would require refactoring, so it's good to take that into account upfront.

Next edit addListenerFor( Key ) to look like this:

Let's go through what happens here:

  1. If the listener requesting to be registered already exists, the registration attempt is blocked and false is returned;
  2. assuming that the request is new, we then get or create a Signal, and a SignalSignature;
  3. if the relevant collection of signatures does not already exist in the _signatures Dictionary, it created and set locally;
  4. the signature is set in said relevant collection of signatures;
  5. the signal itself starts listening via Signal's listen() function; and
  6. either true or false is returned, depending on success or otherwise.

At this point we can add a listener to a signal. Now lets add the functionality to remove one.

Next add the following private function:

_removeSignalFor( Key ) simply deletes a signal by nullifying its listeners, and then removing references to it in our dictionaries, all to be safe for memory management purposes.

Next add the following private function:

Every signal has the array property listeners, and _annulEmpty( Signal ) simple deletes any signal which has nothing left listening to it, by querying the count property of listeners. As you can see, it calls _removeSignalFor( Key ).

Next add the following private function:

As you can see, it calls _annulEmpty( Signal ). Let's go through what happens here:

  1. If we are trying to remove a non-existent listener, just block the call;
  2. Otherwise stop listening by just calling Signal's removeListener() function;
  3. Finally, assess whether this was the last listener this signal had, and if so delete it.

We are now in a position to remove listeners.

Add the following code to removeListenerFor( Key ):

This simply calls the private function _removeListenerFor( Key ). And as in the process of creating the functionality to remove listeners we created the private function _removeSignalFor( Key ), we are now also in a position to remove signals.

Add the following code to removeSignalFor( Key ):

This simply checks if we have a given signal, and if so deletes it. So that's listeners added and removed, and signals added. All that's left to do is dispatch a signal.

Add the following code to dispatchSignalFor( Key ):

Signals creator Tuomas Artman says only that => is a "special operator" which can be used to fire signals. I have not been able to find any more information on this special operator, more information would be gratefully received if you have any.

With those functions added the code is complete. Here is the full class without line breaks:

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: