Library assisted by a Plugin to reduce some of the complexity/boilerplate around DependencyInjection. It is designed to also work well in a modularized code-base.
Your Dependencies can be defined (mostly) in the TypeSystem
TODO: Add more explanation
- Add a dependency on the
DependencyInjection
target - Apply the plugin
DependencyInjectionPlugin
to target - Declare a type as Injectable by conforming to one of these protocols
Injectable
Whenever this is injected a new "instance" will be constructedSingleton
Only one instance will be created, once created it will be kept in memoryWeakSingleton
Only one instance will be created, once the instance is no longer referenced it will be deallocated
- Use
@Inject
or@Assisted
on arguments of a types initializer
To demonstrate how to use SwiftDependencyInjection
lets start with a example.
class APIService: Injectable {
init() {}
func request(url: URL) -> Data
}
class UserRepository: Injectable {
let apiService: APIService
init(@Inject apiService: APIService) {
self.apiService = apiService
}
func listUsers() -> [User]
}
class UserListViewModel: Injectable {
let repository: UserRepository
init(@Inject repository: UserRepository) {
self.repository = apiService
}
}
// Now instead of this
let apiService = APIService()
let repository = UserRepository(apiService: apiService)
let viewModel = UserListViewModel(repository: repository)
// You can just write
let viewModel = Dependencies.global.userListViewModel()
// the `newInstance` method is automatically generated
When a type is declared as Injectable
a new instance of it will be created whenver it is injected.
If thats not the desired behaviour a type can be declared as either Singleton
or WeakSingleton
Singleton
Only one instance will be created, once created it will be kept in memoryWeakSingleton
Only one instance will be created, once the instance is no longer referenced it will be
class UserRepository: Singleton {
let apiService: APIService
init(@Inject apiService: APIService) {
self.apiService = apiService
}
func listUsers() -> [User]
}
// You can obtain a reference to it using
let repository = UserRepository.getInstance()
In some situations types cannot be injected by conforming to any of the protocols. Since the conformance has to be done in the type declaration (not in a extension) this is the case when you need to inject types from a third-party library.
In this case you can declare manual bindings
To do that you need to add a extension to one of these types
Dependencies.Factories
Dependencies.Singletons
Dependencies.WeakSingletons
and declare a static method named bind
the return type declared by the method can now also be injected.
All arguments of that method will be injected (like when using the @Inject
annotation in a Initializer)
extension Dependencies.Bindings {
// This makes ThirdPartyLibrary.SomeClass usable with @Inject
func someClass() -> ThirdPartyLibrary.SomeClass {
return ThirdPartyLibrary.SomeClass()
}
// This is also useful if you want to inject a protocol
func bind(repository: UserRepository) -> UserRepositoryProtocol {
return repository
}
}