Exploring Dependency Injection Design Pattern in iOS

Greetings! I am Kevin, an accomplished iOS Software Developer with expertise in software development, architecture, testing, maintenance, and debugging of complex software systems. I have more than five years of experience in developing high-quality iOS apps using Swift programming language, UIKit, SwiftUI and Combine frameworks.
With a keen eye for detail and a strong understanding of the latest cutting-edge development tools and procedures, I am dedicated to delivering exceptional results that meet or exceed the needs of my clients. I have a proven track record of working both independently and collaboratively as part of a productive team, and I am able to effectively self-manage during independent projects.
Thank you for visiting my personal blog, where I look forward to sharing my knowledge and expertise in iOS development and contributing to the vibrant developer community.
๐ Introduction
In a good code design is very important to use proper abstractions because they make our code loosely coupled. This means that different components of our code can be replaced with alternative implementations without affecting other parts.
Loosely coupled code is the main goal of Dependency Injection. It allows us to write code that is easier to test, extend, and reuse. All of this makes our code easier to maintain.
The Dependency Injection design pattern aims to reduce dependencies between components by allowing dependencies to be injected from the outside rather than being created or managed internally.
Dependency injection cannot be understood without the Dependency Inversion principle. In simple words, this principle states that implementation details should depend on higher-level abstractions and this is essential for creating loosely coupled applications.
In the Dependency Injection design pattern, dependencies are "injected" into a class through constructor injection, property injection, method injection, or ambient context
๐ท Types of Injection
1. Constructor Injection: Dependencies are passed to a class through its constructor.
2. Property Injection: Dependencies are set through properties or variables of a class.
3. Method Injection: Dependencies are provided through method parameters.
4. Ambient Context : Single globally accessible dependency that is exposed and can be used by a lot of different clients in an app.
๐จ Diagram

๐จ๐ผโ๐ป Implementation
In the following example, we are going to see the most suggested and used use-case of the Dependency Injection through the constructor (initializer).
We have a DataManager class that depends on a DataSourceProtocol.
The DataManager is decoupled from specific implementations of the DataSourceProtocol, making it more flexible and easier to test.
We provide the appropriate implementation of the DataSourceProtocol during the initialization of the DataManager using in this case the constructor injection.
// A protocol defining a data source
protocol DataSourceProtocol {
func fetchData() -> String
}
// A concrete implementation of the DataSource protocol
class RemoteDataSource: DataSourceProtocol {
func fetchData() -> String {
return "Data from remote server"
}
}
// Another concrete implementation of the DataSource protocol
class LocalDataSource: DataSourceProtocol {
func fetchData() -> String {
return "Data from local storage"
}
}
// A class that depends on the DataSource protocol and
// injects it using constructor injection
class DataManager {
private let dataSource: DataSourceProtocol
init(dataSource: DataSourceProtocol) {
self.dataSource = dataSource
}
func displayData() {
let data = dataSource.fetchData()
print("Fetched data: \(data)")
}
}
To use the DataManager in from our code example, you can follow these steps:
// Create instances of the data sources
let remoteDataSource = RemoteDataSource()
let localDataSource = LocalDataSource()
// Create an instance of DataManager with a remote data source
let dataManager = DataManager(dataSource: remoteDataSource)
dataManager.displayData()
// Fetched data: Data from remote server
// Create another instance of DataManager with a local data source
let anotherDataManager = DataManager(dataSource: localDataSource)
anotherDataManager.displayData()
// Fetched data: Data from local storage
โ Positive aspects
1. Testability: With Dependency Injection, it's easy to provide mock or stub implementations of dependencies during testing, allowing for comprehensive unit testing.
2. Modularity and reusability: By injecting dependencies, components become more modular and can be reused in different contexts or scenarios.
3. Flexibility: Dependency Injection makes it easier to switch or substitute dependencies without modifying the consuming class. This promotes flexibility and makes the code more maintainable.
โ Negative aspects
1. Increased complexity: Introducing Dependency Injection can add complexity to the codebase, especially in larger projects. Managing dependencies, their lifecycles, and configuration can become challenging.
2. Indirect dependencies: Dependency Injection can result in a chain of dependencies, making it harder to trace and understand the flow of data throughout the application.
3. Increased setup and boilerplate code: Dependency Injection often requires writing additional code for dependency management, configuration, and injection, which can increase the overall codebase size.
๐ Conclusions
Dependency Injection is a powerful design pattern that promotes loose coupling, testability, and modularity.
By allowing dependencies to be injected from the outside, it enhances flexibility and makes the code more maintainable.
However, it's important to strike a balance and carefully consider the complexity and overhead it introduces to ensure the benefits outweigh the drawbacks.
The specific implementation and usage of Dependency Injection can vary based on the requirements and architectural choices of your iOS project.
If you want to be notified of the upcoming articles you can subscribe to the Newsletter and support The iOS Mentor blog using Buy Me a Coffee.
You also have the option to support this blog as a sponsor, contributing to the growth of our iOS Development community.
I am also available on LinkedIn and GitHub so let's connect!
Thanks for reading everyone and enjoy the rest of your day! ๐



