How to reproduce Dagger functions or develop your own DI in Kotlin?

Illustration by Magdalena Tomczyk

As an Android developer, you must have heard of Dagger. Moreover, you’ve probably faced some difficulties with setting it up in Kotlin projects. What are the benefits of this library? Do we still need those Dagger’s annotation processors and code generators? Being inspired by Antonio Leiva great article on how to use Dagger 2 on Android With Kotlin and Elye’s Dagger 2 for Dummies in Kotlin, as well as relying on my own experience, I’ll try to convince you that actually we don’t need them at all.

In this article I’ll show you how basic Dagger features can be easily replaced.

Setup

The concept of skipping Dagger in Kotlin deserves its own name. Double “g” for the similarity with “Dagger” and “K” as in “Kotlin”. I called it Kagger, and surprisingly, in Danish kager means cakes 🍰. It’s not a library, so assuming you’ve already defined Kotlin dependencies, you have nothing to configure at all. Or solution will be pure-kotlin and therefore a piece of cake :)

source: imgflip.com

Components and modules

Just like in Dagger, we are using “components” and “modules” terms. The main difference is we split them to their interfaces and implementations. The following comparison should be self-explanatory.

Dagger

In Dagger you would probably define module like:

@Singleton annotation is used to mark single-instance beans, so function provideSomeSingleton() will be called only once. The second one, provideSomeBean() is not annotated as singleton and will be invoked every time it’s being injected. Let’s imagine that we have another few modules, such as AxModule, BxModule and CxModule.

When it comes to a component, you have to define a list of contained modules and specify which classes will be able to use that component:

Then you can build your component:

And finally you are able to inject your dependencies:

PowerManager and LocationManager will be accessible after OnCreate call.

Note: In the newest version of Dagger, it’s not necessary to access components by casting application property. I used it to make example clear.

Kagger

How to achieve the same only with Kotlin?

Firstly, define module interface:

Please notice I’ve replaced singleton declaration with a field. That’s the way we emphasize the difference between singletons and non-singletons to avoid misleadings in the future. It’s up to you if you follow this pattern or not. When the definition is done, let’s make an module implementation:

What is more, for singleton field, lazy property was used. It’s worth to know it’s thread-safe and we can be sure that object will be instantiated only once.

Let’s move on to component interface:

It may seem familiar to Dagger component declaration. Again, apart from its name, we are declaring a list of modules that component should contain. It’s not necessary to specify which classes will be able to “inject” those objects.

For similarity with above Dagger example, let’s implement a component as an object expression:

Here we are using the true power of Kotlin — delegation. MyComponent interface extends AxModule, BxModule and CxModule. Thanks to the implementation by delegation we are keeping those modules apart and we don’t have to implement all methods and fields in one place. Each module can contain its own dependencies, as AxModule which uses application context.

You may create separate class for component implementation as well:

So MyApplication class simplifies to:

If you do so, the access to items looks like this:

As you can see above, we are “injecting” items without annotation or lateinit keyword. Vals remains vals. In case you want to parameterize non-singleton objects, just add a parameter to a module function:

For testing purposes, you can create a mocked version of MyComponent and replace only necessary modules, methods or fields.

To help you with better understanding of this matter, I’ve put a compilable project to GitHub repository: https://github.com/marcin-jelenski/kagger

Why does it matter?

With the mechanism described above, we were able to remove simple Dagger dependencies in projects created for Indoorway. What’s more, Kagger may be used outside of Android projects. I’m looking forward for your opinions and thoughts on this.

--

--