Clean Swift体系结构概述

读者好!

在本文中,我将讨论iOS应用程序的体系结构-Clean Swift 。 我们将考虑主要理论要点,并在实践中分析一个实例。



理论


首先,我们将分析体系结构的基本术语。 在Clean Swift中,应用程序由场景组成,即 每个应用程序屏幕都是一个场景。 场景中的主要交互经过ViewController- > Interactor- > Presenter组件之间的顺序循环。 这称为VIP周期。

组件之间的桥梁是Models文件,该文件存储传输的数据。 还有一个Router ,负责在场景之间传输和传输数据,还有Worker ,它接管了Interactor逻辑的一部分。



检视


通过代码编写的情节提要,XIB或UI元素。

ViewController


仅负责View的配置和交互。 控制器不应包含任何业务逻辑,网络交互,计算等。
它的任务是在Interactor中使用View处理事件,显示或发送数据(不进行处理和检查)。

牵连器


它包含场景的业务逻辑。

它与网络,数据库和设备模块一起使用。

Interactor从ViewController接收一个请求(数据为空或为空),对其进行处理,并在必要时将新数据传输到Presenter

主讲人


他从事显示数据的准备工作。

例如,在电话号码中添加掩码或在标题大写的第一个字母。
它处理从Interactor接收到的数据,然后将其发送回ViewController

型号


VIP周期的各个组件之间传输数据的一组结构。 循环的每个圈具有3种类型的结构:

  • 请求 -数据结构(来自TextField等的文本),用于从ViewController传输到Interactor
  • 响应 -具有数据结构(从网络等下载),以便从Interactor传输到Presenter
  • ViewModel-具有在Presenter中处理的数据(文本格式等)的结构,用于传输回ViewController

工人


如果Interactor迅速增长,则卸载Interactor ,承担应用程序的部分业务逻辑。

如果在多个场景中使用了它们的功能,则还可以创建所有场景都通用的Worker。

例如,在Worker中,您可以使使用网络或数据库的逻辑成为可能。

路由器


所有负责场景之间的过渡和数据传输的逻辑都在Router中提取。



为了阐明VIP周期的情况,我将举一个标准示例-授权。

  1. 用户输入用户名和密码,然后单击授权按钮
  2. ViewController触发IBAction ,然后使用输入到TextFields中的用户数据创建一个结构,(模型->请求)
  3. 创建的结构被传递给Interactor'e中fetchUser方法
  4. Interactor向网络发送请求,并接收有关授权成功的响应
  5. 根据接收到的数据,使用结果创建一个结构(“模型”->“响应”),并将其传递给Presenter'e中presentUser方法
  6. Presenter根据需要设置数据格式,然后将其返回(模型-> ViewModel)到ViewController'e中displayUser方法
  7. ViewController将接收到的数据显示给用户。 在授权的情况下,可能会显示错误或使用路由器触发到另一个场景的转换

因此,我们得到了一个单一且一致的结构,并将职责分配到各个组件中。

练习


现在,让我们看一个小的实际示例,该示例显示VIP周期如何进行。 在此示例中,我们将在打开场景(屏幕)时模拟数据加载。 我用注释标记了代码的主要部分。

整个VIP周期与协议相关,协议可在不中断应用程序的情况下更换任何模块。
ViewController创建了DisplayLogic协议,该链接传递给Presenter以便以后调用。 对于Interactor ,创建了两个BusinessLogic协议,它们分别负责从ViewControllerDataSource调用方法,用于存储数据并通过Router将另一个场景传输到Interactor 。 Presenter订阅了PresentationLogic协议以从Interactor进行呼叫。 所有这些的连接元素是Models 。 它包含一些结构,借助这些结构可以在VIP周期的各个组件之间交换信息。 我们将以此开始代码分析。



型号


在下面的示例中,对于Home场景,我创建了一个HomeModels文件,其中包含VIP循环的一组查询。
FetchUser请求将负责加载用户数据,我们将进一步考虑。

// Models
/// VIP
enum HomeModels {
/// VIP
enum FetchUser {
/// Interactor View Controller
struct Request {
let userName: String
}
/// Presentor Interactor
struct Response {
let userPhone: String
let userEmail: String
}
/// View Controller Presentor
struct ViewModel {
let userPhone: String
let userEmail: String
}
}
}

ViewController


初始化类后,我们将实例化此场景的InteractorPresenter类,并在它们之间建立依赖关系。
此外,在ViewController'e中,仅存在指向Interactor的链接。 使用此链接,我们将创建一个对Interactor中fetchUser(request :)方法的请求 ,以开始VIP周期。

在这里值得注意对Interactor的请求是如何发生的。 在loadUserInfromation()方法中,我们创建Request结构的实例,并在其中传递初始值。 可以从TextField ,表等中获取。 Request结构的实例传递到fetchUser(request :)方法,该方法位于InteractorBusinessLogic协议中。

// ViewController
///
protocol HomeDisplayLogic: class {
///
func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel)
}
final class HomeViewController: UIViewController {
/// Interactor'a
var interactor: HomeBusinessLogic?
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
///
private func setup() {
// VIP
let interactor = HomeInteractor()
let presenter = HomePresenter()
//
interactor.presenter = presenter
presenter.viewController = self
// Interactor View Controller
self.interactor = interactor
}
override func viewDidLoad() {
super.viewDidLoad()
//
fetchUser()
}
/// Interactor
private func loadUserInfromation() {
// Interactor
let request = HomeModels.FetchUser.Request(userName: "Aleksey")
// Interactor'a
interactor?.fetchUser(request)
}
}
/// HomeDisplayLogic
extension HomeViewController: HomeDisplayLogic {
func displayUser(_ viewModel: HomeModels.FetchUser.ViewModel) {
print(viewModel)
}
}

牵连器


Interactor类的实例包含指向PresentationLogic协议的链接,在此协议下对Presenter进行了签名。

fetchUser(request :)方法可以包含任何数据加载逻辑。 例如,我刚刚创建了带有假定获得的数据的常量。

使用相同的方法,将创建Response结构的实例,并使用之前获得的参数填充该实例。 使用presentUser(response :)方法将响应传递到PresentationLogic 。 换句话说,这里我们获取了原始数据,并将其传递给Presenter进行处理。

// Interactor
/// Interactor'a
protocol HomeBusinessLogic: class {
///
func fetchUser(_ request: HomeModels.FetchUser.Request)
}
final class HomeInteractor: HomeBusinessLogic {
///
var presenter: HomePresentationLogic?
func fetchUser(_ request: HomeModels.FetchUser.Request) {
//
//
let userPhone = "+7 (999) 111-22-33"
let userEmail = "im@alekseypleshkov.ru"
// ...
// Presentor'
let response = HomeModels.FetchUser.Response(userPhone: userPhone, userEmail: userEmail)
// Presentor'
presenter?.presentUser(response)
}
}

主讲人


它具有指向DisplayLogic协议的链接,在该协议下对ViewController进行了签名。 它不包含任何业务逻辑,而仅在显示接收到的数据之前对其进行格式化。 在该示例中,我们格式化了电话号码,准备了ViewModel结构的实例,然后使用DisplayLogic协议中的displayUser(viewModel :)方法将其传递给ViewController ,其中已在其中显示数据。

///
protocol HomePresentationLogic: class {
/// Interactor'a
func presentUser(_ response: HomeModels.FetchUser.Response)
}
final class HomePresenter: HomePresentationLogic {
/// View Controller'a
weak var viewController: HomeDisplayLogic?
func presentUser(_ response: HomeModels.FetchUser.Response) {
//
let formattedPhone = response.userPhone.replacingOccurrences(of: "-", with: " ")
// ViewModel View Controller
let viewModel = HomeModels.FetchUser.ViewModel(userPhone: formattedPhone, userEmail: response.userEmail)
// View Controller'a
viewController?.displayUser(viewModel)
}
}

结论


通过这种体系结构,我们有机会分配职责,提高测试应用程序的便利性,介绍实现的各个部分的可替换性以及编写团队协作代码的标准。

感谢您阅读到最后。

系列文章


  1. 了解Clean Swift架构(您在这里)
  2. Clean Swift体系结构中的路由器和数据传递
  3. 干净迅速的建筑工人
  4. Clean Swift架构中的单元测试
  5. 一个简单的在线商店架构Clean Swift的示例

场景的所有组成部分: 链接
撰写文章的帮助: Bastien

Source: https://habr.com/ru/post/zh-CN453986/


All Articles