Inject —Scalable dependency injection with Swift + support for singletons

Daniel Illescas
ITNEXT
Published in
6 min readMay 25, 2020

--

Dependency injection using Inject

Hello everyone!, thanks @Yves Sinkgraven for the opportunity of writing articles to ITNEXT.IO.

Inject is a small library I made a few days ago which enables an easy way of injecting your dependencies using a dependency resolver.

The idea

The idea came as I was reading a medium article which explained how dependency injection could work for Swift using property wrappers.

I’m also learning ASP.NET core with C# and I really liked how easy is to inject dependencies to your services:

Dependency injection on ASP.NET core with C#

With these ideas in mind I decided to make a library for Swift.

But first thing first, what is dependency injection? Do I need to use any external library for that? what does this library offers?

Dependency injection

(this is a just brief explanation, if you already know what is all about you can skip this section)

Basically, lets say you have a view/viewController in your app in which you want to retrieve user data which is fetched from a remote service or a database, so you can later use it on a label, or button, etc. You may want to use the repository pattern, in which you create different classes to encapsulate the logic to access or modify a data source.

One thing you could do is create an instance of that repository in you view, so whenever you want to use some data (retrieve user, modify, etc) you use that repository.

One improvement over this is not to instantiate that repository inside that view class because that view shouldn’t know about how the repository is implemented or created, but rather it just wants to use it. Basicall we should put in practice the Dependency inversion principle, which in this case is as simple as moving the instantiation of the dependency (our repository) to the view constructor, something like MyView(userRepository: SomeUserRepository()).

Another improvement is to create an interface/protocol in Swift for your repository (repository or anything that is a dependency). So you could have a real implementation or a mock implementation to fake some data. Or you could have an implementation that right now uses a local database but later on uses a remote database or just some web services. Having a common interface for your dependencies is a very nice choice.

Easy native dependency injection on Swift

As another improvement, we could even use the factory pattern to create a factory class for our repository, so instead of instantiating the repo directly we do it in a centralized place. Advantages: if you want to change all your repository instantiations used accrosed the app you just change it in one place.

Almost the same case is for singletons, but would need a shared instance of it.

Inject

Before explaining why you might want to use my library, first let me introduce you to a small example on how to add your dependencies and use it. This is the equivalent of what we’ve talked about before but using inject.

Adding dependencies using Inject
Using the dependencies with inject

We add the dependencies by using a protocol and an implementation of it. Later we declare them on the classes we want to use them and use the Inject property wrapper, from this point you can normally use your dependencies inside your classes (you only need to use $yourDependency in your init method, and it’s actually optional to do so).

How it works?

Inject uses a dependency resolver, you must statically instantiate it in order to use it. It basically registers your dependencies and how to build them so it can later resolve it.

Dependency resolvers — Inject

This is how the Inject property wrapper really works, it uses a dependency resolver passed in the constructor, and later when you use the variable it resolves the dependency implementation.

Inject property wrapper

If you don’t want to keep passing your dependency resolver around you can just create a local extension that uses that in an empty constructor.

Also, Singletons

What about singletons? Inject supports them. You just need to create a class (or even a struct and wrap it inside a Box<T> type), create some methods, and add it using a singleton resolver. You can add a type implementation directly or use a protocol, as with previous examples.

Singleton example with Inject

You don’t need a static variable and you can use a protocol for your singleton too.

In this case the singleton resolver will store a reference value to the singleton instance acroos the app. Unlike the normal resolver, which only saves a function to build a new dependency.

But wait! There’s more

Nested dependencies!

Nested dependencies example — Inject

In this case we have a repository which itself depends upong a database manager class or something. Thanks to inject, we just add all the dependencies and magically use the injected CarRepository :)

More stuff

You can also use @AutoWired , which is another property wrapper which doesn’t have a setter. I don’t recommend it too much because you can’t easily change its value as when using inject, but some people might prefer it this way.

Conclusion — Pros, Cons, why you might want to use Inject

Cons:

  • Maybe too much stuff for simple/small projects with a couple dependencies.
  • It’s a project dependency, it needs to be updated as swift does (which it will I hope).

Pros:

  • Easy to use once you set it up.
  • Swift package manager support.
  • It uses some kind of factory pattern but under the hood, you just need to add your dependencies and use them.
  • Special support for singletons, no need to use to create more static variables for them; just use them as normal dependencies!
  • Lazily resolves your dependencies.
  • Great nested dependencies support.

Well, that’a all for today! If you are still interested and think Inject is for you, check its github repo here:

And here is a project example using it:

Thanks for reading! :D

--

--