Clean Architecture + Combine for SwiftUI

Mehran Kamalifard
8 min readMar 11, 2023

--

let’s learn how to implement a Clean architecture in a SwiftUI app

Photo by Amanda Marie on Unsplash

Overview

One of the main goals of building software is to meet the business goal. Whether an extensive scale application or a small one, they live to solve a problem. With the growth of software, many software architectures have come out, Whatever the software architecture is the end goal here is to build strong software that can provide the business solution while being maintainable and easy to test.

The goal here is to create an application that not only satisfies the business requirement but also be flexible enough to adapt to growing changes.

Clean architecture

The concept of Clean Architecture was originally proposed by Robert C. Martin, known also as Uncle Bob. The main purpose of this approach is the separation of concerns, to separate your code into independent layers and design it to depend on abstractions instead of concrete implementations.

In Clean, The software is separated into 3 layers, data layer, domain layer, and presentation layer. The data layer deal with where the data comes from and the presentation layer deals with how the user interface is presented. The business layer in middle is unaware of how or where the data comes from and how the user interface is rendered. This allows us to keep the business logic independent of data and presentation.

MVC, MVP, and MVVM are some of the most popular iOS app development architectures used by many developers. Each architecture has its advantages and disadvantages, which should be considered before starting the project.

Maybe this question arises what is the problem with using MVVM or MVC that we need Clean Architecture?

Don’t take this the wrong way, MVC and MVVM even MVP are solid architectures. They have advantages and disadvantages and could be used to create great solutions, but they have some limitations.

For a better understanding of why each of the architectures we mentioned is ViewModel not always suitable for starting a project, for example, we will discuss the disadvantages of MVC architecture.

In MVC, the controller is the Glue that has lots of duties and responsibilities making the controller bulky, and especially when we use UIKit as the UI framework iOS controller is tightly coupled with View, it was hard to test it as well.

MVC architecture

Also, when using MVC/MVP/MVVM, writing unit tests for them is much harder to separate elements and mock dependencies for testing purposes, and each component is disposed to have multiple responsibilities. If you ever used MVVM in projects, you’ve probably noticed that it’s not a perfect architecture. It lacks some layers like the Router layer, the coordinator pattern can help us to separate and transfer the routing mechanism somewhere else, but essentially using the coordinator will increase the complexity of the architecture.

Getting Started

We know that writing quality software is hard and complicated, It is not only about satisfying requirements, but also should be maintainable, testable, and flexible enough to adapt to growth and change. This is where “the clean architecture” comes up and could be a good approach for developing any software application.

Why the clean approach?

  1. Separation of code into different layers with assigned responsibilities makes it easier for further modification.
  2. High level of abstraction
  3. Loose coupling between the code
  4. Testing of code is painless
  5. UI frameworks exist on the outermost layer and therefore are just a presenter for data passed from internal layers.
  6. You can replace CoreData or Realm or any other database. Your business rules are not bound to the database.

As we know Clean Architecture has different layers in the application. The main rule is not to have dependencies from inner layers to outer layers, There can only be dependencies from the outer layer inward.

clean architecture layers

Presentation Layer

This layer contains coordinators, which drive visual components. So, this is where UI Components generation is. The whole process is conducted by particles and actions which don’t actually have inner logic except UI logic. A presentation may be based on such patterns as Model-View-Controller (MVC), or Model-View-ViewModel (MVVM), which we used MVVM and will go into detail.

In the Demo project, the Presentation layer contains Coordinator, ViewModel, and SwiftUI View.

Navigation (Coordinator Pattern)

To handle the navigation between views, we decided to move it from the views to another place where the coordinator pattern does it. This method works in such a way that the view sends a navigation request to its view model class, and the view model class sends a navigation request to the coordinator class of its flow.

no more coupling between the views and navigations. Plus now a single class is managing the navigation so SRP (Single responsibility principle).

Domain layer

The domain layer is the innermost layer and doesn’t depend on the outer layers. domain layer contains classes that make up the business rules of the application. the domain layer could contain Entities(Business Models), Use Cases, and Repository Interfaces.

Business logic: The code that converts real-world goals or limitations into the software system. Business logic often relates to how data can be created, stored, and changed.

the domain Folder contains entities where business logic exists and the domain layer

Entities are Implemented like the Object you declared typically.

Use Case

It communicates with the repository, processes data, and passes results as a data model to a presenter which we use the view model to prepare data to present.

as we know protocol could act as a blueprint for the implementation of a class or struct We use protocols or interfaces to illustrate intent and enforce the behavior of a class.

Core Layer

The Core layer can contain classes responsible for networking and also helpers. The idea of Clean Architecture is to have high-level layers always depend on the low-level ones and not the other way around. In our app, the Presentation layer depends on the view model and the view model depends on the Business Logic layer, which depends on the Core layer. The Core layer doesn’t know anything about the Business Logic.

Data Layer

The data layer exists to serve as an abstraction over where data is coming from and that should be its only responsibility. Most apps' data layers fetch data from the network and then caching that data in a local database. So to best separate the concerns, a use case should only be concerned about interacting with different sources from the data layer and any business logic.
here we have two sorts of fetching data from resources, in the first step we are going to call some services to get the required data which we will discuss in the next paragraph, now we are going to talk about Data Persistence at the data layer.
To handle data persistence we have used Core Data and a Combine Wrapper above that to manage CRUD orders.

Core Data Entity

The repository could help us with the CRUD operations around the Combine Wrapper to achieve better separation of concern.

as we know we are going to use MVVM to manage communication between Presenter(View) and Usecase Layer.

Core API

As we mentioned above, the core layer also includes the core service, here we did not use Alamofire or other frameworks and decided to use URL Session + Combine to better understand this layer.

URLSession provides the dataTaskPublisher(for:) which returns a publisher that wraps a URL session data task for a given URL.

Calling an API with Combine

The service call method in this type of implementation is that in the first step, you have to create a requester class of Network Target type and create the requirements of that request in that class, and all these requirements are done in the Data layer. It makes the requester class not depend on the Network Client. In the second step, the requester class is introduced to the Network Client class through Generic and the request is sent.

Now it’s time to create a class as Network Client and introduce the Request to it and give all its requirements and use it when needed.

MVVM

An important point that we have not mentioned yet is the connection between the business layer and the presentation layer, which we chose MVVM to implement.

Basically, using MVVM transfers the state and behavior of the view to the view model, which makes writing tests easier.

ViewModel

A base class can help us a lot in implementing child view model classes. This attitude creates dynamic dispatch. But it gives good flexibility to view model classes.

What Did I Develop

The Demo project presents a modern, approach to Clean arch development using SwiftUI and Combine.

We have created an application using a Clean MVVM approach. We also added the Coordinator pattern so that module creation and navigation tasks are not outside the View. We also implemented a Core API using Combine.

Conclusion

I hope this post gave you a good insight into how to separate the project into different layers and use them when you want.

There are many benefits to having a separation between these layers. Not only will the code be easier to read but also be easier to maintain. Testability will improve because you can test the layers separately.

--

--