A small but flexible Dependency Injection library for swift.
To install the FlexInject package through Swift Package Manager(SPM):
- Open Xcode
- Go to File -> Add Packages
- On top right search bar, search for URL: https://github.com/TribalScale/FlexInject.git
- Add the package
FlexInject helps with property style dependency injection using propertWrappers to perform the injection. FlexInject uses a shared or custom container in order to acheive the final Dependency Injection.
FlexInject has a shared container on the DependencyContainer class in order to register our dependencies. To do so we simply call the register method.
DependencyContainer.shared.register(type: NetworkService.self) {
return NetworkService()
}In order to retreive the dependency simply call the resolve method
let dependency = DependencyContainer.shared.resolve(type: NetworkService.self)@Inject is a propertyWrapper that can be used to resolve the dependency to a property. After you have registered your dependency, you can add the propertyWrapper to a property for it to be resolved.
class ClassWithAResolvedProperty {
@Injected
let networkService: NetworkService
}That is all that is required to get the dependency from the shared instance of the container.
@LazyInject is a propertyWrapper that is used to resolve the dependency using a lazy property. It will delay the resolving until the property is first accessed.
class ClassWithAResolvedProperty {
@LazyInject
let networkService: NetworkService
}The first time you use the networkService it will call into the container and retreive the instance.
@WeakInject is a propertyWrapper that is used to resolve the dependency using a weak property. It will hold a weak reference to the property allowing it to fall out of memory when the dependency is removed from the container.
class ClassWithAResolvedProperty {
@WeakInject
let networkService: NetworkService?
}All the propertyWrappers can take in specific String key, custom containers, and a ResolveMode for shared/new.
Instead of using register for dependencies based on their types, we can also register based on a String key. This allows us to register multiple dependencies of the same type.
DependencyContainer.shared.register(key: "NetworkService") {
NetworkService()
}It can then be resolved with any of the following:
let networkService: NetworkService = DependencyContainer.shared.resolve(key: "NetworkService")
@Inject(key: "NetworkService")
var networkService: NetworkService
@LazyInject(key: "NetworkService")
var networkService: NetworkService
@WeakInject(key: "NetworkService")
var networkService: NetworkService?You can pass in your own container to the propertyWrapper as well. This will allow you to have multiple containers.
let container: DIContainer = DependencyContainer()
container.register(type: NetworkService.self) {
return NetworkService()
}
let networkService = container.resolve(type: NetworkService.self)
@Inject(container: container)
var networkService: NetworkService
@LazyInject(container: container)
var networkService: NetworkService
@WeakInject(container: container)
var networkService: NetworkService?This mode allows you to either request a shared or new instance of the dependency. Each mode will do the following:
new- Rerun the closure to return a new instance of the dependencyshared- Create an instance using the closure and then return that dependency in the future to anyone else requesting thesharedinstance.
let networkService = DependencyContainer.shared.resolve(type: NetworkService.self, mode: .new)
@Inject(mode: .new)
var networkService: NetworkService
@LazyInject(mode: .shared)
var networkService: NetworkService
@WeakInject(mode: .new)
var networkService: NetworkService?Note: Using the @WeakInject and the ResolveMode.new will mean that you will need to manage the non-weak reference of the object. Of course that is the nature of the weak type.
In order to unit test with this framework, you simply register your mock dependencies in place of the real ones.
DependencyContainer.shared.register(type: NetworkService.self) {
return MockNetworkService()
}This will now resolve the dependency in the class you are testing to the mock instance of your NetworkService.
For more information on how this library was made, check out my blog post here