iOS Interview: Dependency Injection [Types and implementation]

Ravi Ranjan
CodeX
Published in
3 min readDec 20, 2022

Have you ever faced these below questions when you start giving an interview for an iOS developer job?

DI

1. What is dependency injection?

2. What are the benefits of dependency injection?

3. what are the types of dependency injection?

And surprisingly you have used dependency injection somewhere in your code but you are unable to explain it with the concept.

So let’s start understanding dependency injection.

Dependency Injection is a software design pattern in which an object receives other instances that it depends on.

So the question comes is if everything was working fine by creating an object of the class and then using the property and function why do we need dependency injection? And the reason behind it is that dependency injection gives the power to write more testable, reusable code which helps us to write maintainable software where we can perform the test with mocked data and it will simplify testing. it is a design pattern that involves providing a component with its dependencies rather than having the component itself create or locate them. This can be useful for a number of reasons, including:

  • It makes it easier to test components in isolation, as you can inject mock dependencies rather than having to set up the real ones.
  • It allows you to decouple components from each other, making it easier to reuse and maintain them.
  • It can make it easier to manage complex systems, as you can define the dependencies at a central location rather than having them scattered throughout the codebase.

There are several ways to implement dependency injection in Swift, depending on the needs of your application. Some common approaches include:
1. Constructor injection: This involves passing the dependencies as parameters to the component’s initializer. below is the example for Constructor injection.

class NetworkClient {
func performRequest(url: URL) {
// Perform network request
}
}

class UserService {
let networkClient: NetworkClient

init(networkClient: NetworkClient) {
self.networkClient = networkClient
}

func fetchUsers() {
let url = URL(string: "url")!
networkClient.performRequest(url: url)
}
}

let networkClient = NetworkClient()
let userService = UserService(networkClient: networkClient)
userService.fetchUsers()

In the above example, the UserService depends on a NetworkClient to perform network requests. The NetworkClient is injected into the UserService via the initialiser, and the UserService can then use the NetworkClient to perform the request.

2. Property injection: This involves setting the dependencies on the component as properties after it has been initialized.

class UserService {
var networkClient: NetworkClient?

func fetchUsers() {
let url = URL(string: "url")!
networkClient?.performRequest(url: url)
}
}

let networkClient = NetworkClient()
let userService = UserService()
userService.networkClient = networkClient
userService.fetchUsers()

3. Method injection: This involves passing the dependencies as arguments to the method that needs them.

class UserService {
func fetchUsers(using networkClient: NetworkClient) {
let url = URL(string: "url")!
networkClient.performRequest(url: url)
}
}

let networkClient = NetworkClient()
let userService = UserService()
userService.fetchUsers(using: networkClient)

4. Interface injection: This involves defining an interface for the dependencies, and having the component implement that interface. This can be useful when the component needs to use multiple dependencies that have a similar roles, but different implementations. For example:

protocol NetworkClient {
func performRequest(url: URL)
}

class HTTPNetworkClient: NetworkClient {
func performRequest(url: URL) {
// Perform network request using HTTP
}
}

class UserService {
var networkClient: NetworkClient

init(networkClient: NetworkClient) {
self.networkClient = networkClient
}

func fetchUsers() {
let url = URL(string: "url")!
networkClient.performRequest(url: url)
}
}

let networkClient = HTTPNetworkClient()
let userService = UserService(networkClient: networkClient)
userService.fetchUsers()

If you liked this, click the 💚 and give a clap on this post as much as you can below so other people will see this here on Medium. If you have any queries or suggestions, feel free to comment or hit me on Twitter, or Linkedin

--

--