MHDependencyKit
MHDependencyKit 是一个轻量级库,以工作流方式帮助解决对象之间的依赖关系。
其目标是解决 UIViewController 转换之间的依赖和数据处理复杂性。在核心上,这种抽象不仅限于 UIKit,因此您可以通过仅实现 DependencyResolver
协议来定义自己的依赖解决实现。
特点
- DependencyCoordinator - 负责注册和解析依赖关系的核心类型
- DependencyResolver - 定义一个类型,该类型负责在提供者和消费者之间解析依赖关系的协议
- ConsumerDependencyResolver - 定义一个类型,该类型负责解析不带提供程序的消费者依赖关系
- UIViewControllerDependencyResolver - 两个依次出现的视图控制器之间的解析器
- UIViewControllerConsumerDependencyResolver - 单个视图控制器的解析器
- UIViewControllerContextDependencyResolver - 两个视图控制器之间的解析器,无论它们在应用中出现的位置如何
- UIViewControllerConsumerContextDependencyResolver - 从无(Void)解析到 UIViewController 的解析器,无论它在工作流程中出现的时间如何。可以用于从 AppDelegate 解析初始依赖关系。
- StoryboardSegueDependencyResolver - 给定/storyboard 实例的解析器
安装
Carthage
将 github "KoCMoHaBTa/MHDependencyKit"
添加到您的 Cartfile
中,然后直接将框架嵌入到您的项目中嵌入。
Cocoapods
在 Podfile
中添加 pod 'MHDependencyKit'
Submodules
将子模块添加到您的仓库中,然后直接将框架嵌入到您的项目中嵌入。
手动操作
如何操作(含示例)
以下所有示例都将使用视图控制器,但您可以使用其他类型的控制器。
此库的最大价值在于可以与Storyboard Segues 的自动依赖关系解决和上下文依赖关系一起使用。
注册依赖
您可以通过指定源和目标来解析任何两种具体视图控制器之间的依赖关系。这通常是一个将这些内容放入应用程序入口点(例如 AppDelegate
)的好地方。
//1
let coordinator = DependencyCoordinator.default
//2
let resolver = UIViewControllerDependencyResolver { (source: ContactListViewController, destination: ContactDetailsViewController) in
//3
destination.contactID = source.selectedContact.id
}
//4
coordinator.register(dependencyResolver: resolver)
- 存在一个默认的共享实例 DependencyCoordinator,我们将使用它,但您不必仅限于它。
- 创建一个您选择的依赖解析器实例。确保指定源和目标类型 - 例如,我们显示从列表中选择的联系人的详细页面。
- 解析两个具体视图控制器之间的依赖关系
- 将依赖解析器注册到协调器
使用可配置协议
指定具体类型既容易又快捷,但是如果您有一个具有许多类型的复杂 UI 和工作流程,您可能会遇到无穷无尽的组合。
对于常见的依赖关系,使用可配置协议是一种更普遍的方法。
//1
protocol ContactIDConfigurable: class {
var contactID: String? { get set }
}
//2
class MyViewController: ContactIDConfigurable {
var contactID: String?
}
class MyOtherViewController: ContactIDConfigurable {
var contactID: String?
}
//3
let coordinator = DependencyCoordinator.default
//4
let resolver = UIViewControllerContextDependencyResolver { (source: ContactIDConfigurable, destination: ContactIDConfigurable) in
//5
destination.contactID = source.contactID
}
//6
coordinator.register(dependencyResolver: resolver)
- 我们通常需要传递一个特定的联系人 ID,因此我们定义了一个可配置协议来表示这种依赖关系。
- 使您的自定义视图控制器符合可配置协议
- 存在一个默认的共享实例 DependencyCoordinator,我们将使用它,但您不必仅限于它。
- 创建一个您选择的依赖解析器实例。确保指定源和目标类型,作为可配置协议
- 解析两个对象之间的依赖关系
- 将依赖解析器注册到协调器
使用这种方法可以定义屏幕之间常见的依赖关系的单个注册。
当与 UIViewControllerContextDependencyResolver 一起使用时,这种方法的价值将大大提高,因为该解析器允许解析工作流程中屏幕位置的依赖关系。
使用故事板插座和上下文依赖关系自动解析依赖关系
一个钩子附加到 UIViewController.prepare(for:sender:),用于解析源和目标视图控制器之间的依赖关系。
在这种情况下使用 UIViewControllerContextDependencyResolver 非常方便,因为它将解析控制器之间的依赖关系,无论其在工作流程中的位置。
您需要考虑的唯一因素是,如果您重写了 UIViewController.prepare(for:sender:) - 您必须调用 super。
注意
请注意,对于每个已注册的解析器,同一时间只能存在一个上下文。这意味着一旦开始一个新的上下文(例如,满足源要求)- 旧上下文将被清除。这意味着在以下情况下 X -> A(源 1) -> B -> C -> D(目标 1) -> E -> F(源 2) -> G
- 上下文将从一个源 1 开始,这应该结束在目标 1
- 一旦到达 D - 上下文将被解析
- 在那个点上,直到 E - 如果你返回 - 然后再到达 D - 它仍然会被解析,因为上下文依然存在。
- 如果你跳过 F(源 2) 并到达 G - 这将开始一个新的上下文,所以如果你在 D 之前返回 - 再次出现 D 将不会被解析
Storyboard Segues 拓展
Storyboard 另一个强大的扩展是能够执行具有内部一次性依赖关系解析的 Segue,如下所示:
//1
class MyViewController: UIViewController {
@IBAction func buttonTapped() {
//2
self.performSegue(withIdentifier: "showNextScreen", sender: nil) { (source, destination: MyAwesomeViewController) in
//3
destination.title = "My Awesome Screen"
}
}
}
- 我们位于自定义视图控制器中,有一个通过界面触摸触发示例动作
- 我们通过使用新扩展方法之一来执行自定义 Segue,该方法允许我们提供一个闭包以解析目标依赖关系
- 我们设置目标标题
在这里的精彩之处在于,在大多数情况下,您将知道下一个屏幕是什么,并且您也有当前屏幕的上下文。
请注意,这种解析仅是临时的,并且在本次调用范围内有效,例如,它不会由其他工作流动作调用。
手动依赖关系解析
如果您不使用 storyboards 或只有一些特殊的案例,您需要手动创建和管理工作控制器 - 您也需要手动解决依赖关系。
//1
let coordinator: DependencyCoordinator = ...
//2
let contactList: UIViewController = ...
let contactDetails: UIViewController = ...
//3
coordinator.resolveDependencies(from: contactList, to: contactDetails)
- 设置和注册您的依赖关系
- 设置您的视图控制器并为解决两个视图控制器之间的依赖关系做准备
- 使用协调器从 contactList 手动解决 contactDetails 的依赖关系
示例即将推出。