Clean Swift Architecture: Is It Really Good for Your App?
- 14581 views
Nowadays, users expect their mobile devices to be even more powerful than desktops. They want their phones to do everything from waking them up in the morning to recognizing their faces via facial recognition technologies.
These rapidly growing expectations have triggered a generation of complex apps with large codebases that integrate multiple components. Mobile apps of this complexity have placed in doubt the existing architectural patterns including Model–View–Controller (MVC).
What’s wrong with MVC?
Developers often refer to MVC as Massive View Controller. There’s a reason for it. In MVC, the controller is thought to update the data in the View and capture events generated by the user. But in reality, the controller
- updates data in the View;
- captures events generated by the user;
- is a delegate of diverse services;
- processes input data;
- is responsible for navigation between screens;
- manages data flow between screens.
Model–View–Controller leads to large classes and contravenes SOLID principles: each component has lots of responsibilities. Big apps built on MVC are also hard to debug and modify.
Another huge downfall of MVC is that it hardly works with unit tests. Since the View is bound up with the Controller, you’ll probably test only the Model. To test the View, you have to play around with replacing the View with mock objects and imitating the lifetime of the View. You should also write code for the View Controller so that the business logic is separated from the View Layout code as much as possible.
You also need to remember that MVC is a UI-related pattern. If you’re building a complex application, you should take everything not related to the UI out of MVC triplets and move it to any other classes, subsystems, or layers.
To solve these problems in iOS apps, we’ll consider the Clean Architecture approach.
Have you heard about Clean Architecture?
Introduced by Uncle Bob, Robert Cecil Martin, in 2012, Clean Architecture is a set of recommendations on how to build an app. Clean Architecture encompasses the ideas of several other architectural approaches and says that an app’s architecture must be:
- Independent from frameworks ‒The architecture should not rely on any library so that you can use frameworks as tools rather than having your system be driven by their limitations.
- Testable ‒ Business logic should be tested without any external elements like interfaces, databases, or servers.
- Independent from the interface ‒ The interface should be easy to change without changing the rest of the system. For example, the web interface should be able to be replaced with the console interface without having to change the business logic.
- Independent from the database ‒ Business logic shouldn’t be tied to а specific databases.
In this scheme, we can see the following layers:
- Entities ‒ This layer contains business logic common to all applications. In the case of a separate application, this layers refers to the most basic business objects.
- Use Cases ‒ application logic that controls the flow of data from the previous layer.
- Interface Adapters ‒ This layer contains adapters between Use Cases and the outside world. It converts data into a format suitable for external layers, such as the web or databases. It also transforms external data into a format suitable for internal layers.
- Frameworks and Drivers ‒ This external layer contains the UI, frameworks, tools, databases, and so on.
All layers are bound by the dependency rule. The dependency rule tells us that inner layers should not depend on outer layers. That is, our business logic and application logic should not be pegged to the Presenter, UI, databases, or other elements. Nothing from an external layer can be mentioned by the code of an internal layer.
This rule allows us to build easy-to-maintain systems as changes in the external layers won’t affect the internal layers.
For iOS development, the original concept of Clean Architecture was a bit modified. Let’s look under the hood at Clean Swift.
What do you need to know about Clean Swift?
Clean Swift is implemented in the VIP and VIPER models positions themselves as a replacement for the MVC pattern. In our article, we’ll talk about the VIP model (View‒Interactor‒ Presenter), as it’s one of our favorite patterns. This model has the following look:
The standard VIP model consists of a View Controller, Models, a Router, a Worker, an Interactor, a Presenter, and a Configurator.
The View Controller starts and ends the VIP cycle. It’s partly isolated. The View Controller sends data to the Interactor, though it doesn’t get responses from the Interactor. This class has a one-sided interaction with the Presenter as well. The View Controller gets responses from the Presenter but can’t transfer anything to it.
The Models class is related to each component in the VIP model. Models is a class containing such structures as Request, Response, and ViewModel:
- Request – A request model contains parameters sent to the API request, which are user inputs such as text entered in text fields and values chosen in pickers.
- Response – This type of model receives the response from the API and stores the appropriate data.
- ViewModel – This model encapsulates responses sent to the Presenter in primitive data types such as String and Int.
We’ll consider the different VIP components using the example of an iOS app that generates random cocktail recipes.
The Router deals with transitions by passing data between view controllers. If you use Segue, you can simplify the initialization of the transitions by calling the Router methods from Prepare for Segue. The Router knows how to transfer data and does this without the extra cycle code from the Interactor/Presenter. Data is transferred using the data store protocols of each module implemented in the Interactor.
The Router also performs setup of the next View Controller and takes care of wiring and delegation setup.
The Worker handles all the API and Core Data requests and responses and prepares data for the Interactor. It also sends success and error responses to the Interactor.
The Worker frees the Interactor from certain tasks. For instance, you can put the logic of database interaction in the Worker. This is especially useful when the same queries to the database can be used in different modules.
The Interactor is an intermediary between the Worker and Presenter. First, it communicates with the ViewController, which passes all the query parameters required for the Worker. Before sending data to the Worker, the Interactor checks this data. If everything is good, the Worker returns the response and the Interactor sends a response to the Presenter.
The Presenter is responsible for presentation logic. It decides how data will be presented to the user. The Presenter organizes the response sent by the Interactor into view models suitable for display. Next, the Presenter passes those view models back to the View Controller to display to the user.
The Configurator is a class that initializes all the Clean Swift components. Yet this class is optional. You can configure scenes by creating a private function in ViewController.
Now that you know the main components of Clean Architecture, it’s time to consider the main benefits and drawbacks of this approach. We’ll talk about the goodies first.
Advantages of Clean Swift
#1 Ready-made templates
Clean Swift has ready-made templates that streamline module creation and save developers’ time. These templates automatically generate files for either one or all components of the VIP model with all bindings between them.
#2 Unidirectional flow of data
Clean Swift realizes a unidirectional flow of data processing and decision making, meaning that there’s always only one cycle: View–Interactor–Presenter–View. Since most of the time you have to change fewer entities, Clean Swift simplifies refactoring. Given that, the Clean approach is best for refactoring projects in which the logic is often changed or complemented.
In Clean Swift, you can easily replace the module with which your interaction takes place with a mock object. You can do this by determining the functionality of each module via a protocol. Additionally, the business logic gets put into a single place instead of being scattered around.
If we simultaneously create business logic and related tests (Interactor, Interactor tests), this makes it possible to do test-driven development (TDD). Since the output and input of each logic case are defined by the protocol, you can simply write a test that determines its behavior and then implement the method logic directly.
You can put most common tasks, such as getting users, into the Worker and then reuse the same workers in different scenes. This way if the processing logic is modified or you need to make additional changes, you can amend the logic in one place but these changes will be available for all scenes.
Clean Swift divides logical units into small pieces. Each developer has their own task. When several developers work on one project and make some changes in the code, these changes have minimal impact on each other. This approach helps to eliminate conflicts inside the team and avoid development delays.
Despite all the advantages mentioned above, Clean Swift still has some downfalls you need to be aware of.
Disadvantages of Clean Swift
#1 Barriers to entry
Even though good documentation for Clean Swift exists, switching from MVC to VIP can take lots of time. You’ll face way more classes that you need to bind somehow. Thus, some developers encounter difficulties in understanding this pattern and use old architectural patterns like MVC that fit a small number of apps.
#2 Over engineering
Initially, Clean Architecture was designed for big server-side applications. This approach requires you to create many entities even for a small feature. Thus, you may end up with too many files. Clean Swift doesn’t fit mobile client-side applications of medium complexity with no intent of further development.
Should you use Clean Swift?
Clean Architecture is gaining traction among iOS developers. Clean Swift is a smart and effective solution for
- long-term projects;
- projects with a generous amount of logic;
- projects for both macOS and iOS platforms;
- projects you want to reuse in the future.
This approach, though, isn’t suited for small projects. So you need to thoroughly evaluate your project and only then decide on the architectural pattern.
If you want to get more news devoted to iOS development, subscribe to our blog.
Subscribe via email and know it all first!