Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Dependency Injection in Swift

If you’ve been programming for a while, you’ve probably stumbled on the term Dependency Injection. It sounds scary at first, but don’t panic, it’s a very simple idea. However, you may be wondering what it is and how it may help you.

On the most basic level, dependency injection means asking an external object to provide dependencies to another object instead of creating an object’s dependencies internally. This means you would have an Injector object that knows how to create dependencies and use them to create new functional objects. In this sense, dependent objects must be modelled differently to receive their components from the Injector class. Let’s take a look:

With this procedure, the Injector class can provide the MessageService component when creating an instance of User.

A simple Injector implementation would be something like this:

In case you want to create an instance of User, you would only ask the Factory class to provide it. For instance, let user = Factory.provideUser()

This helps you create and maintain a consistent way of generating new User objects through the project, and also to keep the User class more manageable as the creation code is located in only one place.

Why use it?

One of the immediate benefits is that the class becomes fully testable. Developers can provide mocked dependencies or stubs to provide reliable and expected responses. We could create a MockMessageService that implements MessageService and pass it to the User and everything would continue to work as expected.

Creation logic gets separated from the object logic making the code more maintainable and reducing boilerplate code in application objects since the work to initialize dependencies is handled by the provider component.

A beneficial side effect of creating new classes that are dependency injection ready-to-use is that when we start developing we think on reusable components that can be passed around and turned into building blocks of bigger objects.

Swift Dependency Injection Framework

Swinject — a full swift Dependency Injection library that has all the power of Swift behind it — enables the use of simple functions to create dependencies in your app. It also supports constructor and property injection plus storyboard injection (it injects dependencies into ViewControllers after they are created).

Using Swinject

At the most basic level, Swinject is composed by a Container object where the component is registered through a closure that contains a factory.

To register a protocol implementation, it should be this way:

container.register(MessageService.self) { _ in EmailService() }

This will provide an instance of EmailService when a MessageService is required. Below we mention 2 ways you can use for sending dependencies to the registered components.

  1. When the dependency was previously registered:

Here, the resolver is an object used to find registered dependencies inside the registration block.

  1. If the component requires one or many external arguments passed to the constructor:

Where the user class is:

When sending arguments to resolve a Dependency be especially careful about the type of parameter. If the sent parameter type doesn’t match the expected type in the registration, the resolve will return nil and show a console error.

Dependencies must be registered before they are used, typically the container is created in the AppDelegate and stored in a public variable so that other classes can get access to the container.

Storyboard Injection

Storyboard injection is done through a Swinject extension called SwinjectStoryboard (https://github.com/Swinject/SwinjectStoryboard). SwinjectStoryboard automatically injects dependencies into view controllers instantiated by storyboard. Dependencies are registered into a Container:

Note that registrations are not done with the registermethod but with the storyboardInitCompleted. This container is passed as parameter when you need to create an instance of SwinjectStoryboard which is used to instantiate ViewControllers:

Main is the name of the storyboard to create and the container passed is the container where the ViewController registrations were made.

UIWindow and RootViewController

There are 2 ways of managing UIWindow and RootViewController instantiation.

Implicit instantiation

When you have the Main storyboard file base name property item in the Info.plist of your app, you must create a SwinjectStoryboard extension and register available ViewControllers in the defaultContainer available in the class func setup.

Note that all view controller dependencies must be available in the defaultContainer or the resolver will not be able to create them. And when instantiating a new view controller make sure you use the SwinjectStoryboard.create(name: “Main”, bundle: nil) method without the container argument, as it defaults then to defaultContainer where your registrations are.

Explicit instantiation

When manually creating a UIWindow instance and setting the root view controller, just create the root view controller using SwinjectStoryboard.create method.

Any of these configurations work for apps, now if you do testing and need to swap some dependency implementations, working with a single container makes it really difficult, so how do we create several containers that include several related services and such modularizing registration.

Using Assemblies

Assembly is a protocol that provides a container where registrations can be made, and later all registrations will be grouped into an unique Assembler.

We created two separate assemblies that manage different registrations and you can notice that SessionViewModel in ViewModelAssembly depends on a Database component declared in another Assembly but despite all assemblies are later passed into the Assembler, the resolution of the component can be made anyway.

How do we use Swinject at Quadion?

In most Swift projects we generally keep a reference to the assembler and the viewControllersContainer in the AppDelegate, and then expose these properties to view controllers through the use of extensions.

By applying this method, we can instantiate other view controllers and be sure they are properly injected:

And to manually create a component from a view controller you should apply the following:

You will have a consistent method of instantiating view controllers and make sure that when you change the way a dependency gets created, it will be replicated in the whole project at once, creating a more maintainable and robust app.

Check out Swinject’s Github repositories:

  • Swinject/Swinject
  • Swinject/SwinjectStoryboard

Would you like to know more? Do you need our help? Contact Us!
www.quadiontech.com


Dependency Injection in Swift was originally published in Quadion Technologies on Medium, where people are continuing the conversation by highlighting and responding to this story.



This post first appeared on Quadion Technologies, please read the originial post: here

Share the post

Dependency Injection in Swift

×

Subscribe to Quadion Technologies

Get updates delivered right to your inbox!

Thank you for your subscription

×