Dependency injection, in general, is beneficial for your application because it promotes loosely coupled architecture and improves testability. There are several ways of implementing dependency injection, but I believe property dependency injection is the most versatile and easy to use one. This library is an implementation of property dependency injection where you just need to declare your dependencies and use them as properties in your classes. No constructor parameters, no extra code.
For Cocoapods, add the following line to your Podfile:
pod 'PropertyInjector'
For Carthage, add the following line to your Cartfile:
github "stanfeldman/PropertyInjector"
You need to make sure that your dependencies get registered before they are injected. You can create a manager class and register the dependencies in its constructor.
class DependencyManager {
init() {
DependencyResolver.register {
$0.singleton(MyDependency())
// or
$0.singleton(MyInterface.self, MyImplementation())
}
}
}
And add this manager to your AppDelegate
for UIKit projects and App
object for SwiftUI projects.
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private let dependencyManager = DependencyManager()
}
Just declare your dependency object as a property and add @Inject
property wrapper. The dependency will be lazily resolved when the object is used.
class ViewController: UIViewController {
@Inject private var dependency: MyDependency
}
There are 2 supported resolution strategies:
factory
creates your dependency every time it is resolvedsingleton
creates your dependency only once and reuses the instance
DependencyResolver.register {
$0.factory(MyDependency())
$0.singleton(MyDependency())
}
You can register a dependency with parameters.
DependencyResolver.register {
$0.factory(MyDependency.self) { parameters in
return MyDependency(name: parameters["name"] as! String)
}
}
And then resolve it using some parameters:
@Inject(with: ["name": "Boris"]) private var dependency: MyDependency
or
let sub2: SubDependency2 = DependencyResolver.resolve(with: ["name": "Boris"])
Automatic property injection using @Inject
is preferable, but it is possible to manually resolve a dependency.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dependency1: MyDependency1 = DependencyResolver.resolve()
let dependency2: MyDependency2 = DependencyResolver.resolve(with: ["uuid": UUID().uuidString])
}
}
Dependencies are resolved on demand so there is no problem injecting class B into class A and class A into class B as long as both dependencies are not used in their constructors.
PropertyInjector will try to resolve an unwrapped type for optionals.
class ViewController: UIViewController {
@Inject private var dependency: MyDependency? // will try to resolve as MyDependency
@Inject private var dependency: MyDependency! // the same
}
To run the example project, clone the repo, and run pod install
from the Example directory first.
PropertyInjector is available under the MIT license. See the LICENSE file for more info.