Dependency Injection in Swift with Property Wrappers
A Modern Approach to Dependency injection in Swift using Property Wrappers.
In this article I will propose an approach to implement Dependency Injection in swift using the new Property Wrappers, without any external API.
Some Background
If you don’t know what dependency injection is or you’re looking for some more simple and quick approach, check out my previous article
So to explain why that approach is not good for every situation let’s show the example I’ve made there
And we inject the dependencies as follows
The Problem with This Approach
This Contstructor Injection would be good if we’re developing a small app, but imagine if our app gets bigger, we would introduce a lot of boilerplate code by calling all the initializers and constantly passing objects to the initializers.
With Swift 5.1 Property Wrappers were introduced, and they could help us to implement dependency injection with a different approach.
If you’re curious to know what property wrappers are and why they were introduced check the following link.
The proposed Solution
I know you’re pretty confused right now but I will show you step by step how I implemented dependency injection with a custom property wrapper
Let’s use again CoffeeMaker as an example to have a better understanding of the problem related to a real world use case.
We have our coffee maker which contains some components as a Pump and a Electric Heater.
Let’s define a Component protocol with a simple serve() function:
Let’s now create our coffee maker components:
And of course our CoffeeMaker class:
As you may have noticed, our components have the @inject annotation, that for those who are familiar with android and dagger2 is not completely new.
But where is that annotation coming from?
Here the Property Wrappers come to the rescue:
What’s happening here?
We’re creating a property wrapper that applies the previous logic to every property we flag with the @Inject annotation.
Based on the component type, our Resolver class will inject the right dependency into our CoffeeMaker.
The “resolve” method will basically return the right dependency from the factory dictionary that we will populate using the “add” method.
Let’s Make Some Coffee
All we have to do now is to register the correct dependencies into our Resolver and try to use our CoffeeMaker
And there’s the output:
I hope you enjoyed the Article, let me know if you have some different approach or any feedback, Thanks!