The first way to do DI is to pass the collaborators in the constructor, where they are then saved in private properties. Let's have as an example on e-commerce app, whose Basket is handled both locally and remotely.
The BasketClient class orchestrates the logic, saves locally in BasketStore, and synchronizes remotely with BasketService:
protocol BasketStore {
func loadAllProduct() -> [Product]
func add(product: Product)
func delete(product: Product)
}
protocol BasketService {
func fetchAllProduct(onSuccess: ([Product]) -> Void)
func append(product: Product)
func remove(product: Product)
}
struct Product {
let id: String
let name: String
//...
}
Then in the constructor of BasketClient, the concrete implementations of the protocols are passed:
class BasketClient {
private let service: BasketService
private let store: BasketStore
init(service: BasketService, store: BasketStore) {
self.service = service
self.store = store
}
func add(product: Product) {
store.add(product: product)
service.append(product: product)
calculateAppliedDiscount()
//...
}
// ...
private func calculateAppliedDiscount() {
// ...
}
}
In Cocoa and Cocoa Touch, the Apple foundation libraries, there are a few examples of this pattern.
A notable example is NSPersistentStore in CoreData:
class NSPersistentStore: NSObject {
init(persistentStoreCoordinator root: NSPersistentStoreCoordinator?,
configurationName name: String?,
URL url: NSURL,
options: [NSObject: AnyObject]?)
var persistentStoreCoordinator: NSPersistentStoreCoordinator? { get }
}
In the end, Dependency Injection as defined by James Shore is all here: define the collaborators with protocols and then pass them in the constructor.
This is the best way to do DI. After the construction, the object is fully formed and it has a consistent state. Also, by just looking at the signature of init, the dependencies of this object are clear.
Actually, the Constructor Injection is not only the most effective, but it's also the easiest.
The only problem is who has to create the object graph? The parent object? The AppDelegate?
We'll discuss that point in the Where to bind the dependencies section.