Singleton vs Dependency Injection in iOS Swift

Prithvi Raj
2 min readJul 4, 2023
Photo by Taras Shypka on Unsplash

Introduction:

In iOS development, managing the creation and sharing of objects is crucial. Two common approaches for managing object instances in iOS are Singleton and Dependency Injection (DI). While both serve the purpose of providing access to objects, they differ significantly in terms of implementation and impact on code flexibility, testability, and maintainability. Lets understand both further.

Singleton:

A Singleton is a design pattern that restricts the instantiation of a class to a single instance and provides a global point of access to that instance. This pattern ensures that only one instance of a class exists throughout the application’s lifecycle, making it available for other parts of the code to access.

Singleton is suitable when:

  • Global accessibility to a shared resource or state is required.
  • Object instantiation and management are straightforward.
  • Few changes or variations in dependencies are anticipated.

Disadvantages of Singleton:

  1. Testability: Singleton can introduce dependencies on shared global state, making it challenging to isolate and test components independently.
  2. Tight coupling: As Singletons are accessed globally, it can lead to tight coupling between classes, making it difficult to change dependencies or substitute implementations.
  3. Thread safety: Care must be taken to ensure thread safety when multiple threads access and modify a Singleton instance concurrently.

Let’s see an example of implementing a Singleton in Swift:

class MySingleton {
static let shared = MySingleton() // Singleton instance

private init() {
// Private initializer to prevent external instantiation
}

func doSomething() {
print("Singleton: Doing something")
}
}

// Usage:
MySingleton.shared.doSomething()

Dependency Injection (DI):

Dependency Injection is a design pattern that focuses on providing objects (dependencies) to a class from external sources rather than creating them internally. It promotes loose coupling between components and enables more flexible, testable, and maintainable code.

Dependency Injection is preferable when:

  • Testability, modularity, and loose coupling are prioritized.
  • Flexibility to configure dependencies at runtime is needed.
  • Significant changes or multiple implementations of dependencies are expected.

Disadvantages with Dependency Injection:

  1. Integration complexity: Adopting Dependency Injection may introduce some integration complexity, especially when integrating with existing codebases or frameworks.
  2. Dependency graph management: As the application grows, managing the dependency graph and ensuring proper configuration of dependencies can become challenging.
  3. Increased code verbosity: Implementing Dependency Injection may require additional code for dependency configuration, potentially increasing code verbosity.

Implementation of Dependency Injection in Swift:

protocol MyDependency {
func performAction()
}

class MyDependencyImplementation: MyDependency {
func performAction() {
print("Dependency: Performing an action")
}
}

class MyClass {
let dependency: MyDependency

init(dependency: MyDependency) {
self.dependency = dependency
}

func useDependency() {
dependency.performAction()
}
}

// Usage:
let dependency = MyDependencyImplementation()
let myClass = MyClass(dependency: dependency)
myClass.useDependency()

Conclusion: Choosing the Right Approach

The choice between Singleton and Dependency Injection depends on various factors such as application size, complexity, testability requirements, scalability needs, and team expertise.

--

--