Embracing Single Responsibility Principle in iOS

Nurul IslamNurul Islam
2 min read

One of the foundational principles of clean code and scalable software design is the Single Responsibility Principle (SRP)—a class should have one, and only one, reason to change. While it sounds straightforward, it often gets overlooked, especially when striving for simplicity in smaller projects. But as applications scale, tight coupling can become a bottleneck for maintainability and testability. Let me illustrate this with an example.

Example of tightly Coupled objects where ViewModel is confirming to protocol A

protocol A {
    func abc()
}

extension A {
    func abc() {
        print("Hi")
    }
}

class ViewModel: ObservableObject, A {
    init() {
        self.abc()
    }
}

At first glance, this implementation seems simple and effective. The ViewModel directly implements the functionality of protocol A. However, it introduces tight coupling, making it harder to:

1. Substitute different implementations of A during testing or future requirements.

2. Mock or stub dependencies for unit tests.

3. Scale and extend functionality without changing the ViewModel.

Solution: Dependency Injection

By decoupling the dependency through Dependency Injection, we adhere to SRP, ensuring the ViewModel is only responsible for managing the logic, not creating or maintaining the dependencies:

class ViewModel: ObservableObject {
    var a: A

    init(a: A) {
        self.a = a
        self.a.abc()
    }
}

Now, the ViewModel doesn’t care how A is implemented. Whether it’s a mock implementation for unit tests or a fully-fledged service class for production, it simply uses the injected dependency.

Why is this Better?

1. Testability: You can inject mock implementations of A to test the ViewModel without relying on the real service.

2. Scalability: When requirements change, you can introduce new implementations of A without modifying the ViewModel.

3. Readability: Each component has a clear, focused responsibility, improving the overall structure.

Final Thoughts

Applying SRP and embracing Dependency Injection may feel like over-engineering for small projects, but as your application grows, you’ll thank yourself for these early decisions. Clean, modular code not only reduces maintenance costs but also accelerates onboarding for new team members and speeds up testing cycles.

1
Subscribe to my newsletter

Read articles from Nurul Islam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nurul Islam
Nurul Islam

I'm an experienced mobile application developer with over 10 years of expertise in iOS development using Swift, Objective-C, and Flutter. Throughout my career, I've had the opportunity to work for multiple renowned companies where I developed and architected robust mobile applications used by millions of users worldwide. In addition to my extensive iOS experience, I also have experience working with ASP.Net Core, Entity Framework, and LINQ for backend development as well as Machine Learning and Deep Learning using Python and TensorFlow which has allowed me to collaborate closely with different teams to develop different types of products, apps or services.