How to apply Single Responsibility Principle in Swift
Sometimes, when we are learning how to code, it’s difficult to understand the classes responsibilities concept. That’s the reason because our first projects become unmaintainable, containing many lines by class in our code and, much more critical, many amount of responsibilities.
A good way to really know the responsibility for a class is thinking in scalability.
Let’s see an example of how to use that. But before I would like to introduce a bit of theory and explain why this concept is important in software design.
Introduction to Single Responsibility Principle
The idea of each class having a single responsibility in a software project, and that responsibility being encapsulated in one unique class has a name: Single Responsibility Principle
This is one of the 5 main principles of software design SOLID where, in Object Oriented Programming, they try to define a guide to have a more understandable, flexible and maintainable software.
These principles are:
- Single responsibility principle
- Open/closed principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle
The author of these concepts, Robert C. Martin, (he wrote one of the most important books in software architecture, Clean Code) talks about ” A class should have only one reason to change”, so he defines a responsibility as a reason to change.
For instance, let’s consider a module that compiles and prints a report. Imagine such a module can be changed for two reasons. First, the content of the report could change. Second, the format of the report could change. These two things change for very different causes; one substantive, and one cosmetic.
The Single Responsibility Principle says that these two aspects of the problem are really two separate responsibilities, and should therefore be in separate classes or modules. It would be a bad design to couple two things that change for different reasons at different times.
The reason it is important to keep a class focused on a single concern is that it makes the class more robust. Continuing with the foregoing example, if there is a change to the report compilation process, there is greater danger that the printing code will break if it is part of the same class. (Example extracted from Wikipedia )
Why is it important to define correctly each class responsibility
If we define our classes knowing which is their responsibility in our project we can:
- Easily understand which functionality it does in each part of the code.
- Modify existent logic faster and in detail.
- Find with less problems the origin of the bugs or unwanted behaviours.
- Abstract logic in different classes or modules.
- Split without major problems implementations so they can be completely replaced later.
- Define unit tests by class or module in a more efficient way, so we can test a little piece of the code, and no more that we really want to test.
Thinking in the scability to define responsibilities
As I said before, you can think in the classes scability to define responsibilities. This is as simple as thinking if in our project might happen that the requirements will be modified, and look in our architecture how these modifications would take place.
If we see that for a small view change we have to modify or touch business logic, we are not correctly defining the responsibilities in our project, for instance.
Let’s see a concrete example in Swift.
Let’s say that I have an app which it shows a list of items from a store. By
now I only have a
ItemsViewController which is in charge of all the logic of
that flow, the data and the presentation of that. Also, it prints a log when
the user selects an item.
ItemsViewController: A simple list of items
You can see the code in https://github.com/fedejordan/SRPExample
To do that,
ItemsViewController uses a
UITableView to show the items in a
list. Also we’re using a
UITableViewCell subclass called
to show these elements.
But let’s say that we want to change the view, for instance, with a
UICollectionView. What would be the problem in this situation?
The view code is very coupled to the items data logic. If we change the view it ‘s very likely that we’ll modify the class in charge of the items as well.
The real problem is in these lines:
let item = items[indexPath.row]
What is the problem here?
Because we are using the
UITableView index to get the specific item in the
array. We should abstract in some way that the view uses an
To avoid that, we’ll refactor
ItemsViewController and we’ll move the
model logic to another class called
The interactor concept has its origin in VIPER architecture. As it is said in the definition, an Interactor contains the business logic to manipulate model objects (Entities) to carry out a specific task. In this case, our
ItemsInteractor is in charge of retrieving information about any
You can get this code in https://github.com/fedejordan/SRPExample, in the
As we can see,
ItemsViewController doesn’t know anything about the data
model. It simply ask the Interactor what it needs to draw the view.
With this approach we can, for example, change the data type
Item for any
other thing, and we would only change
ItemsInteractor. It would retrieve the
same information (we wouldn’t change the public methods) and finally
ItemsViewController could keep working as always.
So, with that refactor, if we want to change just the layout of our app, we
ItemsViewController to use an
ItemsViewController screenshot, using a UICollectionView
You can see the final result in https://github.com/fedejordan/SRPExample ,
Did we change something in
ItemsInteractor? Just nothing. We simply changed
the presentation of the feature.
This, also, allow us to test all in a more modularized way. We can first start
to test the view, and then the business logic. Of
course that, for doing tests correctly, we need to do something more
injectable, se we can initialize the modules with mocked classes and we depend
of interfaces or
protocols in Swift. Basically it means, for instance, not
ItemsViewController, because it’s an
dependency, and it should be abstracted from its implementation. But all of
this exceeds the article purpose.
Seems that we didn’t do so much work. We simply removed the view logic from the data model. Was it really worth it? In this example, maybe it wasn’t too useful, in fact, could be that we simply need a list and nothing else.
But when we have more complex UIs, we have to adapt the same view to different data sources, or share the data model with different flows. Applying this concept allow us to reuse the code and scale the project in a more proper way. And to do that, it’s important to understand which responsibility should have each class that we maintain.
I would like to add that, in my opinion, this concept is a necessary condition to have a good code. In fact, in any code that is badly written, we will always find any class with more than one responsibility. It’s because of that we saw the importance of Single Responsibility Principle.
Our Swift example allowed us to understand why it’s important to define correctly the responsibilities. How a correct modularized architecture can be the key in the scability of our project, simply because we want to do a layout change.
As a summary I would like to conclude that, looking always in our project scability , we will be able to see if we are correctly defining our classes or modules responsibilities.
Thank you very much for reading the post!
I hope you enjoyed it. You can send any message or suggestion about the topics in the comments section, or simply sending me a mail to email@example.com
This article is also available on Medium