SwiftUI Dependency Injection
Dependency injection is an essential part of programming. There are three ways to inject dependencies:
- Constructor injection
- Property injection
- Method injection
Constructor Injection
Constructor injection means that you define the dependencies in the init of your object. In my opinion, this is the most preferable way of doing dependency injection. This because it is instantly clear what the object depends on when you create an instance of it. It also enables clearer code as you can store the dependencies as a private constant.
Property Injection
Updating a property of an instance with a dependency is called property injection. This means the property needs to be internally/publicly settable and in most cases is nil by default. When you look at these kind of classes it is harder to reason about as its always a question if the property is nil or has changed in the meantime.
Method Injection
Method injection is similar to Property Injection, but the main difference is that you use a method that updates the property. The main advantage of this is that you can make the property private so it is not possible to get the dependency from outside the object. This makes your api a little bit safer.
Why not always use Constructor Injection?
When you have a dependency cycle it is impossible to use Constructor Injection. A dependency cycle means that object A depends on object B and visa versa. You cannot initialise either of them because they both rely on each other. Switching one of the two objects to Property or Method Injection fixes this problem.
Using SwiftUIDI
Add SwiftUIDI to your project with Swift Package Manager (SPM).
Now we know what types of injection there are, let's have a look what SwiftUIDI uses.
SwiftUIDI is build on property injection. Because we can use property wrappers nowadays, it is possible to do property injection with less of the disadvantages mentioned above.
The @Injected property wrapper makes it impossible to change the dependency form within an object. Another benefit is that you don't have to set it yourself, so you don't have to use any of the injection methods mentioned above. Also accidental dependency cycles are impossible as it will trigger a compiler error. Its al taken care for you!
Before you can use the dependency with @Injected, you'll have to register it. First get a reference to the dependencies:
@Environment(\.dependencies) var dependencies: Dependencies
Then you can start adding dependencies:
let userDefaults = dependencies.resolve() as UserDefaults?
To use a dependency in you object you can no use @StateInjected when you want to use it inside of a SwiftUI View. In other cases you can use @Injected.
SwiftUI View:
@StateInjected var userDefaults: UserDefaultsAny other object:
@Injected var userDefaults: UserDefaults
Conclusion
A powerful type safe dependency injection framework that has a very small codebase. It is currently still in development, but please try it out, share your findings and help make it the best dependence injection framework out there.