Solid Principles — Single Responsibility Principles

Savita Agrawal
Evangelist Apps Blog
3 min readApr 4, 2023
Photo by Christopher Gower on Unsplash

In this article I’m aiming to describe the Single Responsibility Principle which is first in the list of SOLID principles. This is very important to understand this if you are looking to create a clean and maintainable project. In order to create Unit tests its important for follow the SRP while you Create the classes.

You can find a detail introduction to SOLID principle in the following article: https://www.scaledrone.com/blog/solid-principles-for-becoming-a-better-ios-swift-developer/

I want to focus on implementing the Single Responsibility principle.

Lets look into the following example:

class EmployeeDatasource {
func getEmployees(completion: @escaping ([SomeEmployees]) -> Void) {
// 1. Create request
let url = URL(string: "SomeEmployee/URL")!
let request = URLRequest(url: url)

// 2. Fetching data
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in

// 3. Parsing data
guard let data = data,
let employees = try? JSONDecoder().decode([SomeEmployees].self, from: data) else {
completion([])
return
}

completion(employees)
}

dataTask.resume()
}
}

This is a data fetching service that fetches some Employees details from a Url. At the moment EmployeeDatasource class is responsible for three tasks :

1. Creating a request

2. Fetching data

3. Parsing data

This one class has three different responsibility. Which makes the whole class difficult to test and it violates the Single responsibility principle.

Lets try to break it down:

URL builder:

class EmployeesURLBuilder {
private let hostName: String

init(hostName: String) {
self.hostName = hostName
}

func getEmployees() -> URLRequest {
let url = URL(string: "\(hostName)SomeEmployees/URL")!
let request = URLRequest(url: url)

return request
}
}

Employee Parser:

struct SomeEmployees: Codable {
let id: Int
let title: String
let position: String
}

class EmployeesParser {
private let decoder: JSONDecoder

init(decoder: JSONDecoder) {
self.decoder = decoder
}

func parse(data: Data) -> [SomeEmployees] {
return (try? decoder.decode([SomeEmployees].self, from: data)) ?? []
}
}

Let see how the datasource class looks like now:

class EmployeesDatasource {
private let requestBuilder: EmployeesURLBuilder
private let parser: EmployeesParser

init(requestBuilder: EmployeesURLBuilder, parser: EmployeesParser) {
self.requestBuilder = requestBuilder
self.parser = parser
}

func getEmployees(completion: @escaping ([SomeEmployees]) -> Void) {
// First responsibility is to Create request
let request = requestBuilder.getEmployees()

// Second responsibility is to Fetch the data
let dataTask = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in

// Third responsibility is to Parsing data
guard let self = self,
let data = data else {
completion([])
return
}

completion(self.parser.parse(data: data))
}

dataTask.resume()
}
}

Now if we look here we can see that we have swapped out the code for building the request and parsing the data for our separate classes. Now our example is following the single responsibility principle. We have 3 classes now:

  1. Class that just creates the request
  2. Class that executes the request
  3. Class to parse the data

Benefits here are:

  • Classes are testable after making the changes. As each class is only responsible for one task we can easily update them and test the same.
  • This gives an ability to re-use these components in other parts of the app or in other apps if we need to

--

--

Savita Agrawal
Evangelist Apps Blog

I'm an iOS Developer and my passion is developing iOS, iPhone and tvOS apps. I'm proficient in C, C++, ObjC and Swift. I enjoy blogging, writting and reading.