Clean Architecture using SwiftUI 5.5
Tags: ios, programming, swift, swiftui
Published: 2021-10-14 10:06:55

By employing [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html), you can design applications with very low coupling and independent of technical implementation details, such as databases and frameworks. That way, the application becomes easy to maintain and flexible to change. It also becomes intrinsically testable. Here I’ll show how I structure my clean architecture projects. This time we are going to build an iOS todo application using SwiftUI.

The folder/group structure of the project takes on the following form:

```
├── Core
├── Data
├── Domain
└── Presentation
```

Let’s start with the **DOMAIN** Layer.
This layer describes **WHAT** the project/application does. Let me explain, Many applications are built and structured in ways that you cannot understand what the application does merely by looking at the folder structure. Using a building of a house analogy, you can quickly identify what a building would look like and its functionality by viewing the floor plan and elevation of the building

![Floor Plan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4y5ocks41nejhk7pcz7.png)

In the same way, the domain layer of our project should specify and describe WHAT the application does. In this folder, we would keep use models, repository interfaces, and use cases. 

```
├── Core
├── Data
├── Presentation
└── Domain
    ├── Model
    │   ├── Todo.swift
    │   └── User.swift
    ├── Repository
    │   ├── TodoRepository.swift
    │   └── UserRepository.swift
    └── UseCase
        ├── Todo
        │   ├── GetTodos.swift
        │   ├── GetTodo.swift
        │   ├── DeleteTodo.swift
        │   ├── UpdateTodo.swift
        │   └── CreateTodo.swift
        └── User
            ├── GetUsers.swift
            ├── GetUser.swift
            ├── DeleteUser.swift
            ├── UpdateUser.swift
            └── CreateUser.swift

```

1. **Model**: A model typically represents a real-world object that is related to the problem. In this folder, we would typically keep classes to represent objects. e.g. Todo, User, etc
1. **Repository**: Container for all repository interfaces. The repository is a central place to keep all model-specific operations. In this case, the Todo repository interface would describe repository methods. The actual repository implementation will be kept in the Data layer.
1. **UseCases**: Container to list all functionality of our application. e.g Get, Delete, Create , Update

The **PRESENTATION** layer will keep all of the consumer-related code as to **HOW** the application will interact with the outside world. The presentation layer can be WebForms, Command Line Interface, API Endpoints, etc. In this case, it would be the screens for a List of Todos and its accompanying view model.

```
├── Core
├── Data
├── Domain
└── Presentation
    └── Todo
        └── TodoList
            ├── TodoListViewModel.swift
            └── TodoListView.swift
```
The **DATA** layer will keep all the external dependency-related code as to **HOW** they are implemented:

```
├── Core
├── Domain
├── Presentation
├── Data
    ├── Repository
    │   ├── TodoRepositoryImpl.swift
    │   ├── TodoAPIDataSourceImpl.swift
    │   └── TodoDBDataSourceImpl.swift
    └── DataSource
        ├── API
        │   ├── TodoAPIDataSource.swift
        │   └── Entity
        │       ├── TodoAPIEntity.swift
        │       └── UserAPIEntity.swift
        └── DB
            ├── TodoDBDataSource.swift
            └── Entity
                ├── TodoDBEntity.swift
                └── UserDBEntity.swift
```


1. **Repository**: Repository implementations
1. **DataSource**: All data source interfaces and entities. An entity represents a single instance of your domain object saved into the database as a record. It has some attributes that we represent as columns in our DB tables or API endpoints. We can't control how data is modeled on the external data source, so these entities are required to be mapped from entities to domain models in the implementations

and lastly, the **CORE** layer keep all the components that are common across all layers like constants or configs or dependency injection (which we won't cover)

Our first task would be always to start with the domain models and data entities

```swift
struct Todo: Identifiable {
    let id: Int
    let title: String
    let isCompleted: Bool
}
```

We need it to conform to Identifiable as we’re going to display these items in a list view.

Next let’s do the todo entity 


```swift

struct TodoEntity: Codable {
    let id: Int
    let title: String
    let completed: Bool
}
```

Let’s now write an interface (protocol) for the TodoDatasource

```swift
protocol TodoDataSource{    
    func getTodos() async throws -> [Todo]    
}
```
We have enough to write an implementation of this protocol and we’ll call it TodoAPIImpl:


```swift
enum APIServiceError: Error{
    case badUrl, requestError, decodingError, statusNotOK
}

struct TodoAPIImpl: TodoDataSource{
    
       
    func getTodos() async throws -> [Todo] {
        
        guard let url = URL(string:  "\(Constants.BASE_URL)/todos") else{
            throw APIServiceError.badUrl
        }
        
        guard let (data, response) = try? await URLSession.shared.data(from: url) else{
            throw APIServiceError.requestError
        }
        
        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else{
            throw APIServiceError.statusNotOK
        }
        
        guard let result = try? JSONDecoder().decode([TodoEntity].self, from: data) else {
            throw APIServiceError.decodingError
        }
        
        return result.map({ todoEntity in
            Todo(
                id: todoEntity.id,
                title: todoEntity.title,
                isCompleted: todoEntity.completed
            )
        })
    }
}
```

**Note**: this repository’s getTodos function returns a list of Todo. So, we have to map TodoEntity -> Todo:

Before we write our TodoRepositoryImpl let’s write the protocol for that in the Domain layer


```swift
protocol TodoRepository{ 
    func getTodos() async throws -> [Todo] 
}
```

```swift
struct TodoRepositoryImpl: TodoRepository{ 
    var api: TodoAPI 
    func getTodos() async throws -> [Todo] {
        let _todos =  try await api.getTodos()
        return _todos
    }
}
```

Now that we have our todo repository, we can code up the GetTodos use case

```swift
enum UseCaseError: Error{
    case networkError, decodingError
}

struct GetTodosUseCase{
    var repo: TodoRepository
    
    func execute() async -> Result<[Todo], UseCaseError>{
        do{
            let todos = try await repo.getTodos()
            return .success(todos)
        }catch(let error){
            switch(error){
            case APIServiceError.decodingError:
                return .failure(.decodingError)
            default:
                return .failure(.networkError)
            }
        }
    }
}
```

and then in turn we can write our presentation’s view model and view

```swift
@MainActor
class TodoListViewModel: ObservableObject {
    
    var getTodosUseCase = GetTodosUseCase(repo: TodoRepositoryImpl(api: TodoAPIImpl()))
    @Published var todos: [Todo] = []
    @Published var errorMessage = ""
    @Published var hasError = false
    
    func getTodos() async {
        errorMessage = ""
        let result = await getTodosUseCase.execute()
        switch result{
        case .success(let todos):
            self.todos = todos
        case .failure(let error):
            self.todos = []
            errorMessage = error.localizedDescription
            hasError = true
        }
    }
}
```

**Note**: We use the @MainActor attribute for the view model class because we need to run these functions on the main thread, a singleton actor whose executor is equivalent to the main dispatch queue.


```swift
struct TodoListView: View {
    @StateObject var vm = TodoListViewModel()
    
    
    fileprivate func listRow(_ todo: Todo) -> some View {
        HStack{
            Image(systemName: todo.isCompleted ? "checkmark.circle": "circle")
                .foregroundColor(todo.isCompleted ? .green : .red)
            Text("\(todo.title)")
        }
    }
    
    fileprivate func TodoList() -> some View {
        List {
            ForEach(vm.todos){ item in
                listRow(item)
            }
        }
        .navigationTitle("Todo List")
        .task {
           await vm.getTodos()
        }
        .alert("Error", isPresented: $vm.hasError) {
        } message: {
            Text(vm.errorMessage)
        }
    }
    
    var body: some View {
       TodoList()
    }
}

```


![List View](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72r8sycu8tcgtm9me12g.png)

So to recap:

![Flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47sy7llvrf6t62kqd9ba.png)