Detailed analysis of different architectures
Article directory
Traditional MVC architecture
**The traditional MVC architecture divides the system into three parts: model (Model), view (View) and controller (Controller). **The model represents data and data processing logic; the view represents the user interface; the controller receives user input and handles real actions. The advantage of this architecture is that it is simple and easy to understand, easy to develop and maintain; the disadvantage is that when the application scale becomes larger, there will be problems with bloated controllers and mixed business logic, which is not conducive to code reuse and maintenance.
MVVM architecture
**MVVM architecture divides the system into three parts: view (View), view model (ViewModel) and model (Model). ** The view is responsible for rendering and user input; the view model handles the logic and updates the view; the model is related to the data. Through the data binding mechanism, the view model can directly reflect changes to the view, thus achieving the characteristics of low coupling and high reusability. The disadvantage is that the implementation is complicated, two-way data binding needs to be realized, and the amount of code is relatively large.
VIPER Architecture
** The VIPER architecture divides the system into five parts: View, Presenter, Interactor, Entity, and Router. **The presenter is responsible for parsing the user's operation into an executable operation of the interactor; the interactor is responsible for executing specific business logic and returning the result to the presenter; the entity stores the actual business logic, and the routing is responsible for the routing and navigation between modules. The architecture implements the Single Responsibility Principle (SRP), has high code reusability, and is easy to maintain. The disadvantage is that the layering is too detailed, the amount of code is slightly larger, and it is difficult to get started.
Clean Swift (also known as VIP) architecture
** Clean Swift divides the system into six parts: View Controller (ViewController), Presenter (Presenter), Business Logic Processor (Interactor), Entity (Entity), Request (Request) and Response (Response). **Clean Swift is in principle a variant of VIPER. The architecture advocates the Single Responsibility Principle (SRP), achieves the goal of separation of duties, and ensures the testability and maintainability of the code. In Clean Swift, the view controller is only responsible for managing the life cycle of the interface and the processing of UI events, while the actual business logic and data processing are placed in components such as presenters, business logic processors, and entities. The advantage of this architecture is that the responsibilities are clearly layered, the code is concise and readable, and it is easy to read and maintain; at the same time, it provides a unified request, response and error handling interface, which is conducive to improving code reusability and testability. The disadvantage is that you need to follow the conventional programming specification.
demo
The following is a demo of a simple To-do List application written using the Clean Swift architecture, which includes functions such as adding, viewing, editing, and deleting to-do items.
1. Create an Xcode project
- Open Xcode.
- Create a new iOS App project and name it
ToDoListDemo
. Swift
Select Language and User Interface in the project settingsStoryboard
.
2. Create schema files and classes
-
In Xcode, create several Groups to store various parts of the Clean Swift architecture, such as:
Models
,Networking
, andScene
so on. -
In
Scene
the Group, create a new Swift file namedToDoListModels.swift
. In this file define all necessary types, structures and models used in the application.// 创建 TodoItem 结构体 struct TodoItem { var id: Int var title: String var description: String? var isCompleted: Bool var date: Date? } // 创建 TodoListViewModel 结构体 struct TodoListViewModel { var items: [TodoItem] }
-
In
Scene
the Group, create a new Swift file namedToDoListWorker.swift
. This file will contain code that performs network requests and data persistence.protocol TodoListWorkerProtocol { func fetchItems(completion: ([TodoItem]) -> Void) func saveItem(_ item: TodoItem, completion: @escaping (Bool) -> Void) func deleteItem(_ item: TodoItem, completion: @escaping (Bool) -> Void) } class TodoListWorker: TodoListWorkerProtocol { func fetchItems(completion: ([TodoItem]) -> Void) { // 在此处执行网络请求或者数据持久化操作 // 返回获取到的 TodoItem 数据 } func saveItem(_ item: TodoItem, completion: @escaping (Bool) -> Void) { // 在此处执行网络请求或者数据持久化操作 completion(true) } func deleteItem(_ item: TodoItem, completion: @escaping (Bool) -> Void) { // 在此处执行网络请求或者数据持久化操作 completion(true) } }
-
In
Scene
the Group, create a new Swift file namedToDoListInteractor.swift
. In this file create the interfaceTodoItem
for the operationTodoListInteractor
and provideTodoListInteractor
a default implementation for the .protocol TodoListInteractorProtocol { func fetchTodoList() func addTodoItem(item: TodoItem) // 添加更新和删除操作方法 } class TodoListInteractor: TodoListInteractorProtocol { var presenter: TodoListPresenterProtocol? var worker: TodoListWorkerProtocol? func fetchTodoList() { worker?.fetchItems(completion: { [weak self] items in self?.presenter?.presentFetchedTodoList(response: items) }) } func addTodoItem(item: TodoItem) { worker?.saveItem(item, completion: { [weak self] success in if success { self?.fetchTodoList() } }) } // 添加更新和删除操作方法的默认实现 }
-
In
Scene
the Group, create a new Swift file namedToDoListPresenter.swift
. In this file, create an interface that represents the obtained and theTodoItem
manipulated , and provide a default implementation of .TodoListViewModel
TodoListPresenter
TodoListPresenter
protocol TodoListPresenterProtocol { func presentFetchedTodoList(response: [TodoItem]) // 添加更新和删除操作方法 } class TodoListPresenter: TodoListPresenterProtocol { weak var viewController: TodoListViewControllerProtocol? func presentFetchedTodoList(response: [TodoItem]) { let viewModel = TodoListViewModel(items: response) viewController?.displayFetchedTodoList(viewModel: viewModel) } // 添加更新和删除操作方法的默认实现 }
-
In
Scene
the Group, create a new Swift file namedToDoListViewController.swift
. In this file, create the interfaces that represent the user interfaceTodoListViewController
and that represent user inputTodoListViewControllerInput
, and provideTodoListViewController
default implementations for .protocol TodoListViewControllerProtocol: AnyObject { func displayFetchedTodoList(viewModel: TodoListViewModel) } protocol TodoListViewControllerInput { func displayAddTodoItemSuccess() // 添加更新和删除操作方法 } class TodoListViewController: UIViewController, TodoListViewControllerProtocol { var interactor: TodoListInteractorProtocol? var router: TodoListRouterProtocol? // 实现 viewDidLoad() 和其他方法 func displayFetchedTodoList(viewModel: TodoListViewModel) { // 更新用户界面以显示获取到的数据 } // 添加更新和删除操作的默认实现 }
-
In
Scene
the Group, create a new Swift file namedToDoListRouter.swift
. In this file createTodoListRouter
the interface that represents the navigation logic and provideTodoListRouter
a default implementation of the .protocol TodoListRouterProtocol { func navigateToAddTodoItem() // 添加其他导航方法 } class TodoListRouter: TodoListRouterProtocol { weak var viewController: UIViewController? // 实现 TodoListRouterProtocol 中的方法 // 可以使用 NavigationController 或 Modal 方式实现不同的导航 }
3. Generate the view controller and complete the basic logic
-
Create a new
UIViewController
class in Xcode, name itToDoListTableViewController
, and set it as the splash screen in the storyboard. -
Use Interface Builder to create the displayed
TodoItem
list and lay it out and design it accordingly.NOTE: Only part of the code is included in this section. The full code can be found in my GitHub repository:
https://github.com/techsleep/clean-swift-todo-app-demo
. -
In
ToDoListTableViewController.swift
the file, declare and initializeinteractor
,presenter
androuter
.class ToDoListTableViewController: UITableViewController { var interactor: TodoListInteractorProtocol? var presenter: TodoListPresenterProtocol? var router: TodoListRouterProtocol? override func viewDidLoad() { super.viewDidLoad() interactor = TodoListInteractor() interactor?.presenter = presenter presenter = TodoListPresenter() presenter?.viewController = self router = TodoListRouter() router?.viewController = self interactor?.fetchTodoList() // 设置 NavigationController 样式和导航按钮等 } }
-
In
ToDoListTableViewController.swift
the file, followTodoListViewControllerInput
the protocol and add the action methods needed to implement it.extension ToDoListTableViewController: TodoListViewControllerInput { func displayAddTodoItemSuccess() { // 在该回调中处理添加待办事项成功后的操作 } // 添加更新和删除待办事项操作方法 }
-
In
ToDoListTableViewController.swift
the file, implement methods that are called when the user enters an action, such as adding a to-do or navigating to the add page, etc.class ToDoListTableViewController: UITableViewController { @IBAction func addButtonClicked(_ sender: Any) { router?.navigateToAddTodoItem() } // 添加其他用户输入操作方法 }
-
In
TodoListRouter.swift
the file, implement the specific logic of the navigation page.class TodoListRouter: TodoListRouterProtocol { weak var viewController: UIViewController? func navigateToAddTodoItem() { let addTodoItemVC = storyboard.instantiateViewController(withIdentifier: "AddTodoItemViewControllerID") as! AddTodoItemViewController addTodoItemVC.delegate = viewController as? AddTodoItemViewControllerDelegate viewController?.navigationController?.pushViewController(addTodoItemVC, animated: true) } // 添加其他导航 }
4. Complete the rest of the logic
-
In
AddTodoItemViewController
, defineAddTodoItemViewControllerDelegate
andTodoItem
objects, and notify via the protocol thatTodoListViewController
a new todo has been added. -
In
AddTodoItemViewController
, useTodoListWorker
the object to store new to-do items, and then notify via the protocol thatTodoListInteractor
the data has been updated. -
In
TodoListInteractor
, useTodoListPresenter
the object to update the UI to show the new todo.
After completing the above operations, you can use the demo of the To-do List application written in the Clean Swift framework to run normally!