Dependency Injection in Swift
Dependency injection is better understood with the help of an example. So let’s jump directly into it.
In this example, Object A depends on Object B and/or Object C. Therefore, Obj A creates instances of Obj B and/or Obj C inside one of its functions. It would look something like the following.
class A {
let b = B() b.call(withModel: Model) {result in ... }
}
This makes our code testing difficult because if an error occurs in class B, its indirect effect would be in class A. This disregards the principle of code isolation, i.e, code should be testable in isolation.
Thus, dependencies (obj B and/or obj C) are injected (into A) rather than being created. This is called dependency injection where the ready-to-use results from objects B and C are passed back to object A for use.
In terms of unit testing, this makes our testing easier as we can fake our obj B and obj C, to test obj A in isolation. Here we do not inject actual dependency in testing, but create the mocks of dependency and inject into A for unit testing class A.
Types of Dependency Injection
Dependencies can be injected in many ways. However, developers use the 3 most common ways of dependency injections. These are discussed as follows:
- Method Dependency Injection
- Initializer Dependency Injection
- Property Dependency Injection
Consider another example here to understand the above 3 kinds of dependency injection. Let’s say you want to call a web service from a function like below via a button click:
Object Instantiation (Incorrect way of using dependencies by creating them)
func callWebService(model: Model) { let webService = WebService() webService.call(withModel: Model) {result in ... }}
In the above method, the webService object (dependency) is created inside callWebService function of some random class that makes the code testing in isolation difficult.
Method Dependency Injection
The correct way would be to provide the webService (dependency) as a function argument (ready-to-use object) which would be termed as dependency injection since we are injecting the dependency in our object rather than creating it. This type of dependency injection is called Method Dependency Injection.
func callWebService(model: Model, webService: WebService) { webService.call(withModel: Model) {result in ... }}
Initializer Dependency Injection
Another common way to achieve the above result is Initializer Dependency Injection as detailed below:
class WebServiceUtil: WebServiceUtilProtocol { var webService: WebService required init(webService: WebService) { self.webService = webService }
func callWebService(model: Model) { self.webService.call(withModel: Model) {result in ... } }}
Property Dependency Injection
Yet another way to achieve dependency injection.
let vm = ViewModel()vm.webService = WebService()
Hope that makes a difference to our knowledge.
Cheers!