读者好!
在
上一篇文章中,我谈到了
Clean Swift架构的
VIP周期。 现在,我们将讨论最重要的主题之一-场景之间
的数据 转换和
传输 。

理论
路由器组件负责导航和数据传输逻辑,这是场景的一部分(当然是可选的)。 它在
ViewController中与
Interactor和
Presenter一起初始化。
路由器实现了两个协议
-RoutingLogic和
DataPassing ,我们将在其中填充我们的功能。
RoutingLogic应该包含负责过渡到特定场景的方法。
DataPassing包含变量
dataStore ,该变量引用协议
DataStore 。
Interactor Scenes实现了
DataStore协议并使用其中存储的变量。
路由器本身包含一个指向其场景的
ViewController的链接。
使用到
ViewController的链接,
路由器在场景之间跳转。 为此,您可以使用
Segue或以编程方式创建要向其过渡的场景。 使用哪种方法并不重要,对我们来说,最主要的是要链接到我们要切换到的
ViewController类的实例。
使用到数据存储的链接
,我们将数据从一个场景的
交互器传输到我们要切换到
的场景的
交互器 。 而且,如前所述,
路由器需要知道如何执行此操作。
练习
例如,我们将文本从TextField传输到其他场景的Label。 让我们考虑两种场景之间的过渡方式-根据
Segue和以编程方式。
Router类包含3个方法的语义组:
- RoutingLogic实现中的方法(routeTo)
- 负责导航的方法(navigateTo,不带Segue的过渡)
- 数据传输方法(passDataTo,如果有要传输的数据)

例如,如果我们通过
Segue进行过渡,则当单击按钮时,则在
ViewController中,我们必须重写prepare(for:sender :)方法。 该扩展名将允许您使用
Segue的名称自动从
Router调用方法。
在使用Segue时,可选的prepare(对于:sender :)是可选的。 您可以将其从代码中排除,然后在Router方法中调用performSegue(withIdentifier:sender :) 。 仅当您需要在数据传输中使用Segue时才需要进行准备 。 | final class HomeViewController: UIViewController { |
| // ... |
| |
| override func prepare(for segue: UIStoryboardSegue, sender: Any?) { |
| // Segue |
| if let scene = segue.identifier { |
| |
| // , Router |
| // router?.routeToNAME(segue:) |
| let selector = NSSelectorFromString("routeTo\(scene)WithSegue:") |
| |
| // , |
| // Segue |
| if let router = router, router.responds(to: selector) { |
| router.perform(selector, with: segue) |
| } |
| } |
| } |
| |
| // ... |
| } |
现在,我们终于找到了最有趣的东西-
路由器代码。 该示例包含注释,因此我们将仅考虑关键点。
在此
路由器中,我们处理两个场景
-Home和
Detail 。 从
Home场景的过渡也有两种处理方式
-Segue和
编程方式 。 数据从
Home场景传输到
Detail场景。
必须根据
routeToNAME原则来命名
RoutingLogic协议中的所有方法,其中
NAME是我们在使用
Storyboard时指定的
Segue (标识符)的名称。 这不仅是为了方便使用和美观,而且对于我们在前面重新定义的
prepare(for:sender :) ViewController中的方法选择器也是必需的。
同样,在
HomeRouter类中,
还有一些方法以
NavigationTo和
passDataTo开头 。 前者负责转换逻辑,而后者负责数据传输。 仅当以编程方式完成过渡时才创建navigationTo方法。
在示例中,我们有一个
routeToDetail(segue :)方法。
segue参数
是可选的,因为 该方法包含一个实现,使您无需使用
Segue即可调用它。 在两种过渡情况下,我们都将获得
HomeViewController'a和
HomeDataStore'a Home场景的非可选值,以及指向
ViewController和
DataStore Detail场景的链接。 这里值得注意的事实是,
detailDS是一个变量,并
使用pass- through参数(inout)传递给
passDataToDetail方法。 这很重要,因为 没有
inout,我们将必须将所有
DataStore协议标记为“仅可以使用类来实现”(Protocol DetailDataStore:class),这会带来许多困难,包括捕获牢固的链接。
| |
| import UIKit |
| |
| /// @objc |
| /// prepare View Controller'e |
| @objc protocol HomeRoutingLogic { |
| /// Detail View Controller |
| func routeToDetail(segue: UIStoryboardSegue?) |
| } |
| |
| protocol HomeDataPassing { |
| var dataStore: HomeDataStore? { get } |
| } |
| |
| final class HomeRouter: NSObject, HomeRoutingLogic, HomeDataPassing { |
| |
| // MARK: - Private |
| |
| // MARK: - Public |
| |
| weak var viewController: HomeViewController? |
| var dataStore: HomeDataStore? |
| |
| // MARK: - HomeRoutingLogic |
| |
| func routeToDetail(segue: UIStoryboardSegue?) { |
| if let segue = segue { |
| // |
| // Segue |
| |
| // Detail View Controller |
| // Data Store Router'e |
| guard |
| let homeDS = dataStore, |
| let detailVC = segue.destination as? DetailViewController, |
| var detailDS = detailVC.router?.dataStore |
| else { fatalError("Fail route to detail") } |
| |
| // , , , |
| // "" |
| passDataToDetail(source: homeDS, destination: &detailDS) |
| } else { |
| // , |
| // Segue |
| |
| // Detail View Controller Storyboard'a |
| // Data Store Router'e |
| guard |
| let viewController = viewController, |
| let homeDS = dataStore, |
| let storyboard = viewController.storyboard, |
| let detailVC = storyboard.instantiateViewController(withIdentifier: "Detail") as? DetailViewController, |
| var detailDS = detailVC.router?.dataStore |
| else { fatalError("Fail route to detail") } |
| |
| passDataToDetail(source: homeDS, destination: &detailDS) |
| |
| // , "" |
| navigateToDetail(source: viewController, destination: detailVC) |
| } |
| } |
| |
| // MARK: - Navigation |
| |
| private func navigateToDetail(source: HomeViewController, destination: DetailViewController) { |
| source.navigationController?.pushViewController(destination, animated: true) |
| } |
| |
| // MARK: - Passing data |
| |
| /// destination inout, |
| /// Data Store |
| private func passDataToDetail(source: HomeDataStore, destination: inout DetailDataStore) { |
| |
| // HomeDataStore DetailDataStore |
| destination.message = source.message |
| } |
| } |
结论
仅此而已。 感谢您阅读到底! 如果您想尝试本文,我将在下面留下该项目的链接。
系列文章
- Clean Swift体系结构概述
- Clean Swift架构中的路由器和数据传递(您在此处)
- 干净迅速的建筑工人
- Clean Swift架构中的单元测试
- 一个简单的在线商店架构Clean Swift的示例
链接到项目撰写文章的帮助:
Bastien