组件
组织代码组件,以最小化iOS应用程序中不同功能之间的耦合。
框架由三个主要对象类型和两个支持对象类型组成
Router
- 决定应打开哪个模块Navigator
- 允许无视视图层次结构地展示视图控制器Module
- 完全封装特定功能Builder
- 实例化模块并准备好它们使用ModuleContainer/Container
- 包含并注入依赖到其他对象中
框架的设计是为了让每个组件尽可能通用。您可以自由地用您自己的实现来替换它们,只需遵守现有的协议。
它是做什么的?
这是一个动态构建iOS应用程序的方法,提供了独特的可能
- 灵活的视图展示,允许您在任何地方动态地推送或展示视图
- 简化提取框架并将它们的内部结构与应用程序的其他部分完全分离。您唯一的接触点可以是具有
Container
的Module
- 可以按任意架构模式实现功能模块,无论是
MVC
、MVVM+C
、还是
Redux
,只需使用Module
作为顶级元素即可 - 直接将深链接构建到应用程序结构中
并且它易于测试。
我该如何安装这个程序?
Cocoapods
将此代码添加到您的 Podfile
pod 'Components'
执行 pod install
并准备就绪。
Carthage
将此代码添加到您的 Cartfile
github "bartlomiejn\components"
执行 carthage update --platform ios
并使用您首选的方法将框架集成到项目中。
手动
将库克隆到目录中
git clone https://github.com/bartlomiejn/components
一旦它出现在那里
- 将
.xcodeproj
文件拖放到您的工作区中。 - 编辑应用程序方案设置,在应用程序目标之前添加
Components-iOS
- 在项目设置中打开常规面板,将框架添加到
嵌入式二进制文件
或链接框架或库
如何使用它?
进入您的 AppDelegate
并输入以下代码
import Components
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]?
) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
// 1.
let container = AppContainer()
let navigator = Navigator(window: window!)
let builder = Builder(navigator: navigator, container: container)
let router = Router(navigator: navigator, builder: builder)
builder.router = router
// 2.
router.register(AppModule.self)
router.open(AppModule.self)
return true
}
- 实例化所有顶级对象。最重要的是
Router
,它是每个应用程序模块
的入口点 - 注册并打开模块。
现在我们已经设置了主层次结构,我们可以在AppContainer.swift
中为我们的Module
定义一个注入容器
import UIKit
import Components
class AppContainer: ModuleContainer {
// 1.
private let storyboard = UIStoryboard(name: "Main", bundle: .main)
override init() {
super.init()
// 2.
addModuleInjection(AppModule.self) { [weak storyboard] module in
module.controller = self.storyboard.instantiateInitialViewController() as? ViewController
}
}
}
- 实例化您希望在不同模块之间共享的对象。
- 添加一个将执行属性注入到
Module
中的闭包
现在,我们需要创建一个具体的模块。让我们添加一个名为AppModule.swift
的文件,并将其放入其中
// 1.
import Components
class AppModule: ModuleInterface {
static let route = "app"
private let router: RouterInterface
private let navigator: NavigatorInterface
// 2.
var controller: ViewController!
init(router: RouterInterface, navigator: NavigatorInterface) {
self.router = router
self.navigator = navigator
}
// 3.
func open(_ parameters: AnyDictionary?, callback: ((AnyDictionary?) -> Void)?) {
// 4.
navigator.present(as: .root, controller: UINavigationController(rootViewController: controller))
}
}
- 在
class
(或struct
)中实现ModuleInterface
- 添加所需的依赖项作为属性
open
方法充当您模块的入口点- 用我们的替换根控制器
以上发生了什么?
首先,《Router》已经实例化了《AppModule》。然后,《Builder》使用您之前在《ModuleContainer》中定义的闭包注入了依赖,在这种情况下是《AppViewController》。
一旦实例化完成,`Router` 使用 `AppModule.open` 方法让您在 `AppModule` 内部执行应用程序逻辑。
最后,《Navigator》将一个 `UIWindow` 的 `rootViewController` 替换为您刚刚给出的控制器。
请注意,《AppModule》根本不需要呈现视图。它可能只是对您的 API 进行 HTTP 请求或其他任何逻辑,通过 `callback` 返回一些结果。
默认情况下,《Router》在一个内部串行队列上同步调度的打开调用,并以异步在主线程上分发结束。
我该如何以不同的方式呈现视图?
《Navigator》中的其他呈现模式包括在顶级《UINavigationController》或顶级《presentedViewController》的堆栈中呈现,您可以从应用中的任何位置呈现视图。
我该如何传递参数或获取结果?
使用扩展的 open
方法
router.open(LoginModule.self, ["username": "username", "password": "abc123"]) { result in
if let wasSuccessful = result["result"] as? Bool, wasSuccessful {
happyPath()
} else {
errorPath()
}
}
为什么它的类型是动态的?
您可以只使用 ModuleInterface.route
属性来识别模块,甚至不需要从打开它的文件中导入其定义
router.open("conversation")
router.open("account", ["id": "123456"])
router.open("login") { result in
doSomethingBased(on: result)
}
这在您将框架分解并尝试打开一个模块时非常有用,因为这个模块由于紧密耦合,必须放在主束中。
我想贡献,我该如何做?
我们欢迎所有贡献。如果您有关于功能、改进、修复或希望在现有解决方案中测试一些内容的想法,请只需添加一个问题。