原创:知识探索型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容
目录
- 一、架构简介
- 二、LoginBuilder
- 1、LoginDependency
- 2、LoginComponent
- 3、LoginBuildable
- 4、LoginBuilder
- 三、LoginInteractor
- 1、LoginRouting
- 2、LoginPresentable
- 3、LoginListener
- 4、LoginInteractor
- 5、回顾一下
- 四、LoginRouter
- 1、LoginInteractable
- 2、LoginViewControllable
- 3、LoginRouter
- 4、routeToCreateAccount
- 五、LoginPresenter/LoginViewController
- 1、LoginPresentableListener
- 2、LoginViewController
- 3、LoginInteractor
- 六、Root RIB
- 1、RootBuilder
- 2、RootRouter
- 3、AppComponent
- Demo
- 参考文献
前言
一个Riblets
(肋骨)被设计成这样,那和之前的VIPER
和MVC
有什么区别呢?最大的区别在路由上面。Riblets
(肋骨)内的Router
不再是视图逻辑驱动的,现在变成了业务逻辑驱动。这一重大改变就导致了整个App不再是由表现形式驱动,现在变成了由数据流驱动。
每一个Riblet
都是由一个路由Router
,一个关联器Interactor
,一个构造器Builder
和它们相关的组件构成的。所以它的命名(Router - Interactor - Builder,Rib
)也由此得来。当然还可以有可选的展示器Presenter
和视图View
。路由Router
和关联器Interactor
处理业务逻辑,展示器Presenter
和视图View
处理视图逻辑。
路由的职责
在整个App的结构树中,路由的职责是用来关联和取消关联其他子Riblet
的,至于决定权则是由关联器Interactor
传递过来的。在状态转换过程中,关联和取消关联子Riblet
的时候,路由也会影响到关联器Interactor
的生命周期。路由只包含2个业务逻辑:1.提供关联和取消关联其他路由的方法。 2.在多个孩子之间决定最终状态的状态转换逻辑。
拼装
每一个Riblets
只有一对Router
路由和Interactor
关联器。但是它们可以有多对视图。Riblets
只处理业务逻辑,不处理视图相关的部分。Riblets
可以拥有单一的视图(一个Presenter
展示器和一个View
视图),也可以拥有多个视图(一个Presenter
展示器和多个View
视图,或者多个Presenter
展示器和多个View
视图),甚至也可以能没有视图(没有Presenter
展示器也没有View
视图)。这种设计可以有助于业务逻辑树的构建,也可以和视图树做到很好的分离。
举个例子,骑手的Riblet
是一个没有视图的Riblet
,它用来检查当前用户是否有一个激活的路线。如果骑手确定了路线,那么这个Riblet
就会关联到路线的Riblet
上面。路线的Riblet
会在地图上显示出路线图。如果没有确定路线,骑手的Riblet
就会被关联到请求的Riblet
上。请求的Riblet
会在屏幕上显示等待被呼叫。像骑手的Riblet
这样没有任何视图逻辑的Riblet
,它分开了业务逻辑,在驱动App和支撑模块化架构起了重大作用。
Riblets是如何工作的
在这个新的架构中,数据流动是单向的。Data
数据流从service
服务流到Model Stream
生成Model
流。Model
流再从Model Stream
流动到Interactor
关联器。Interactor
关联器可以向Service
触发变化来引起Model Stream
的改动。这个强制的要求就导致关联器只能通过Service
层改变App的状态。
数据从后台到视图View上
一个状态的改变,引起服务器后台触发推送到App。数据就被Push
到App,然后生成不可变的数据流。关联器收到model
之后,把它传递给展示器Presenter
。展示器Presenter
把model
转换成view model
传递给视图View
。
数据从视图到服务器后台
当用户点击了一个按钮,比如登录按钮。视图View
就会触发UI事件传递给展示器Presenter
。展示器Presenter
调用关联器Interactor
登录方法。关联器Interactor
又会调用Service call
的实际登录方法。请求网络之后会把数据pull
到后台服务器。
树型关系
当一个关联器Interactor
在处理业务逻辑的工程中,需要调用其他Riblet
的事件的时候,关联器Interactor
需要和子关联器Interactor
进行关联。如果调用方法是从子调用父类,父类的Interactor
的接口通常被定义成监听者listener
。如果调用方法是从父类调用到子类,那么子类的接口通常是一个delegate
,实现父类的一些Protocol
。在Riblet
的方案中,路由Router
仅仅只是用来维护一个树型关系,而关联器Interactor
才担当的是用来决定触发组件间的逻辑跳转的角色。
一、架构简介
该架构背后的主要思想是,应用程序应由业务逻辑而不是视图驱动。展示 RIB
的最佳方法是一棵树:每个 RIB
都是一个节点,并且它可以不包含子节点,也可以包括一个或多个子节点。RIB
即 路由 + 交互器 + 构造器 (Router Interactor Builder
)。
-
路由(Router):负责相邻
RIB
之间的导航 -
交互器(Interactor):是处理
RIB
业务逻辑的主要组件。它响应用户交互,与后端对话,并准备数据显示给用户 -
构造器(Builder):是一个将所有
RIB
片段组合在一起的构造器
还有一个可选的视图(View
)和展示器(Presenter
)。View
本身没有任何业务逻辑,它仅负责呈现 UI 并接受传递给 Interactor
的用户交互。Interactor
拥有View
,并且 View
通过委托模式与Interactor
对话。Presenter
基本上是View
实现的协议。
例如,在View
上点击登录按钮将触发Interactor
中的Web
任务,Interactor
将告诉 Presenter
显示活动指示器。登录成功后, Interactor
将告诉 Router
导航到下一个页面。
幸运的是,现在你想要创建一个带有所有组件的 RIB
时,不需要手写所有样板代码。您可以安装和配置 Xcode自带的 Template
解决(通过Falcon
中install-xcode-template.sh
)。
要创建新的 RIB
,只需打开文件创建菜单,然后从列表中选择 RIB
。
我们将创建一个名为 Login
的 RIB
,并勾选 Owns corresponding view
以让 RIB
带有视图。
这个 Xcode 模板会生成以下几个文件。我们接下来会仔细研究它们中的每一个,并讨论它们的功能。
二、LoginBuilder
一个 Builder
负责创建所有 RIB
组件。
1、LoginDependency
import RIBs
protocol LoginDependency: Dependency
{
// TODO: Declare the set of dependencies required by this RIB, but cannot be
// created by this RIB.
}
您会注意到的第一件事是,大多数组件是协议,而不是具体的类。这是 RIB
的主要特性之一,我们将在本文后面讨论。LoginDependency
用于将依赖项从其父项注入到 RIB
。例如,我们有一个 webService
用于执行登录 Web
请求。我们创建一个我们要注入的 WebServicing
协议。
protocol WebServicing: class
{
func login(userName: String, password: String, handler: (Result<String, Error>) -> Void)
}
现在,我们可以更新 LoginDependency
协议,为 Builder
提供对其依赖项的访问。
protocol LoginDependency: Dependency
{
var webService: WebServicing { get }
}
2、LoginComponent
final class LoginComponent: Component<LoginDependency>
{
// TODO: Declare 'fileprivate' dependencies that are only used by this RIB.
}
我们在这里使用的下一个组件是 LoginComponent
。我们可以声明一些仅在此 Builder
中使用的局部变量,例如设置 AdMob ID
等。在我们的示例中不需要这些局部变量,因为我们不需要任何私有依赖项。
3、LoginBuildable
protocol LoginBuildable: Buildable
{
func build(withListener listener: LoginListener) -> LoginRouting
}
下一个协议是 LoginBuildable
,它只有一个方法 build(withListener:)
。这里的 listener
参数是父侦听器。我们可以自由地向此构建方法添加更多参数,只需要根据需求来定制。
4、LoginBuilder
final class LoginBuilder: Builder<LoginDependency>, LoginBuildable
{
override init(dependency: LoginDependency)
{
super.init(dependency: dependency)
}
func build(withListener listener: LoginListener) -> LoginRouting
{
let component = LoginComponent(dependency: dependency)
let viewController = LoginViewController()
let interactor = LoginInteractor(presenter: viewController)
interactor.listener = listener
return LoginRouter(interactor: interactor, viewController: viewController)
}
}
LoginBuilder
类实现了 LoginBuildable
协议,它是这里的主要组件。它使用 LoginDependency
创建一个 LoginComponent
。LoginComponent
现在封装了这个 RIB
需要的所有依赖项。
该构建器还创建一个 LoginViewController
和 LoginInteractor
,用于创建和返回 LoginRouter
。
这里是另一行重要的代码:interactor.listener = listener
。这就是我们将父 Interactor
与子 Interactor
连接的方式。例如,我们有一个与 RootRIB
连接的 LoginRIB
。在这种情况下, RootInteractor
必须实现 LoginInteractor
侦听器将声明的方法。如果 LoginInteractor
有一个 dismissLogin
方法,则根 RIB
将实现此方法以分离Login
流并显示主页。
三、LoginInteractor
同样, Xcode 模板会自动为您生成以下所有代码。
import RIBs
import RxSwift
1、LoginRouting
protocol LoginRouting: ViewableRouting
{
func routeToCreateAccount()
}
LoginRouting
是我们用来从Login RIB
导航到后续RIB
的协议。假设我们希望能够导航到 CreateAccount
页面。
2、LoginPresentable
protocol LoginPresentable: Presentable
{
var listener: LoginPresentableListener? { get set }
// TODO: Declare methods the interactor can invoke the presenter to present data.
}
LoginPresentable
用于响应在 Interactor
中执行的业务逻辑来更新 Login
视图。如果打开 LoginViewController
,您会注意到它实现了此协议。 LoginPresentable
还拥有一个 LoginPresentableListener
实例。这是 LoginViewController
与 Interactor
进行通信并调用业务逻辑的一种方式。换句话说,这是 Interactor
和 ViewController
相互通信的方式。
如上所述,我们希望我们的视图控制器在执行 Web
任务时显示活动指示器。为了实现这一点,我们在 LoginPresentable
中添加了一个新方法 showActivityIndicator
。
protocol LoginPresentable: Presentable
{
var listener: LoginPresentableListener? { get set }
func showActivityIndicator(_ isLoading: Bool)
}
3、LoginListener
protocol LoginListener: class
{
// TODO: Declare methods the interactor can invoke to communicate with other RIBs.
}
最后,我们有一个 LoginListener
。还记得 LoginBuilder
中的这一行代码吗?
interactor.listener = listener
这是 Root RIB
将要实现的侦听器。这是子级 RIB
与父级进行通信的一种方式。登录完成后,我们需要通知 Root RIB
,以便可以取消登录流程。
protocol LoginListener: class
{
func dismissLoginFlow()
}
4、LoginInteractor
final class LoginInteractor: PresentableInteractor<LoginPresentable>, LoginInteractable, LoginPresentableListener
{
weak var router: LoginRouting?
weak var listener: LoginListener?
// TODO: Add additional dependencies to constructor. Do not perform any logic
// in constructor.
override init(presenter: LoginPresentable)
{
super.init(presenter: presenter)
presenter.listener = self
}
override func didBecomeActive()
{
super.didBecomeActive()
// TODO: Implement business logic here.
}
override func willResignActive()
{
super.willResignActive()
// TODO: Pause any business logic.
}
}
现在我们看一下 LoginInteractor
类。它有两个 weak
变量: router
和 listener
。这就是 Interactor
连接到其 Router
和父 Interactor
的方式。可以看到,该Interactor
还拥有一个 Presenter
。
接下来,我们可以看到一些生命周期方法 didBecomeActive
和 willResignActive
。这些方法是自解释的,我们不会直接调用它们。例如,我们可以在 didBecomeActive
中执行Web
任务以获取所需的数据,或者根据我们的业务逻辑进行初始视图设置。
5、回顾一下
RIB
背后的核心思想是该应用程序应由业务逻辑驱动。Interactor
就是此业务逻辑所在的地方。这里是我们使用 Interactor
控制应用程序流程的方式。
- 我们调用
presenter
方法来更新登录 UI(我们的示例中有showActivityIndicator
) - 我们调用
router
方法导航到子RIB
(我们的示例中有routeToCreateAccount
) - 我们调用
listener
方法与父RIB
对话(我们的示例中有dismissLoginFlow
)
四、LoginRouter
1、LoginInteractable
protocol LoginInteractable: Interactable
{
var router: LoginRouting? { get set }
var listener: LoginListener? { get set }
}
LoginInteractable
是这里的主要协议,包含两个组件, LoginRouting
和 LoginListener
。我们在 Interactor
中创建它们。
2、LoginViewControllable
LoginViewControllable
用于操纵视图层次结构。因此,当 Interactor
告诉 Router
使用 LoginRouting
导航到 CreateAccount
时, Router
最终将需要显示 CreateAccount
页面。我们需要添加以下方法。
protocol LoginViewControllable: ViewControllable
{
// TODO: Declare methods the router invokes to manipulate the view hierarchy.
func present(_ viewController: ViewControllable)
}
3、LoginRouter
如您所见,LoginRouter
实现了 LoginRouting
协议,因此我们需要添加必需的方法 routeToCreateAccount
。
final class LoginRouter: ViewableRouter<LoginInteractable, LoginViewControllable>, LoginRouting
{
// TODO: Constructor inject child builder protocols to allow building children.
override init(interactor: LoginInteractable, viewController: LoginViewControllable)
{
super.init(interactor: interactor, viewController: viewController)
interactor.router = self
}
func routeToCreateAccount()
{
...
}
}
4、routeToCreateAccount
在展示其 viewController
之前,我们需要有一个CreateAccount RIB
。创建另一个 RIB
。我们不会在此 RIB
中进行任何更改,因此只需将其保留并返回 LoginRouter
即可。
要构建 CreateAccount RIB
, LoginRouter
需要有一个 CreateAccountBuilder
。声明一个类型为 CreateAccountBuildable
的私有变量,并更新 LoginRouter
构造器,以注入 CreateAccountBuildable
。我们没有使用具体的 CreateAccountBuilder
类型。相反,我们使用协议 CreateAccountBuildable
。
final class LoginRouter: ViewableRouter<LoginInteractable, LoginViewControllable>, LoginRouting
{
private let createAccountBuilder: CreateAccountBuildable
init(
interactor: LoginInteractable,
viewController: LoginViewControllable,
createAccountBuilder: CreateAccountBuildable
) {
self.createAccountBuilder = createAccountBuilder
super.init(interactor: interactor, viewController: viewController)
interactor.router = self
}
func routeToCreateAccount()
{
...
}
}
现在我们可以完成 routeToCreateAccount
方法。我们使用 createAccountBuilder
构建一个 createAccountRouter
。我们需要在 Build
方法中将当前的 Interactor
作为侦听器传递。我们将 createAccountRouter
作为子级附加到当前 Router
。这就是我们构建 RIB
树的方式。我们调用 LoginViewControllable
方法来呈现 CreateAccount
视图控制器。
func routeToCreateAccount()
{
let router = createAccountBuilder.build(withListener: interactor)
attachChild(router)
viewController.present(router.viewControllable)
}
在这里会遇到的第一件事是以下编译器错误:
Argument type ‘LoginInteractable’ does not conform to expected type ‘CreateAccountListener’
要解决此问题,我们需要确保 LoginInteractable
实现 CreateAccountListener
协议:
protocol LoginInteractable: Interactable, CreateAccountListener
{
var router: LoginRouting? { get set }
var listener: LoginListener? { get set }
}
这是另一件要记住的事情。我们使用 attachChild
方法附加 createAccountRouter
。后续需要另一种方法来关闭 CreateAccount
页面。关闭子页面后,我们必须将其 Router
与当前树分离。当 viewController
不再可用时,相应的 RIB
仍在树中,我们不想看到这种状态,因为这最终可能导致内存泄漏和意外行为。为了避免这种情况,我们将在 LoginRouter
中创建一个变量保留对 CreateAccountRouter
的引用,然后将其与当前树分离。
final class LoginRouter: ViewableRouter<LoginInteractable, LoginViewControllable>, LoginRouting
{
private let createAccountBuilder: CreateAccountBuildable
private let createAccountRouter: CreateAccountRouting?
// ...
}
现在,我们更新 routeToCreateAccount
方法。我们需要将 createAccountRouter
保存到本地变量。另外,如果已经创建了子 Router
,我们需要防止再次创建Router
和提供子视图控制器。
func routeToCreateAccount()
{
guard createAccountRouter == nil else { return }
let router = createAccountBuilder.build(withListener: interactor)
createAccountRouter = router
attachChild(router)
viewController.present(router.viewControllable)
}
最后,当我们要关闭 CreateAccount
页面时,在使用视图层次结构进行操作后,我们必须从当前树中分离其 Router
。
func detachCreateAccount()
{
guard let createAccountRouter = createAccountRouter else { return }
createAccountRouter.viewControllable.uiviewController.dismiss(animated: true, completion: nil)
detachChild(createAccountRouter)
self.createAccountRouter = nil
}
Xcode 将显示另一个编译器错误,因此我们需要更新 LoginBuilder
并将 CreateAccountBuilder
传递给 LoginRouter
的构造器。我们使用 LoginBuilder
创建并注入一个子 Builder
。
final class LoginBuilder: Builder<LoginDependency>, LoginBuildable
{
override init(dependency: LoginDependency)
{
super.init(dependency: dependency)
}
func build(withListener listener: LoginListener) -> LoginRouting
{
let component = LoginComponent(dependency: dependency)
let viewController = LoginViewController()
let interactor = LoginInteractor(presenter: viewController)
interactor.listener = listener
let createAccountBuilder = CreateAccountBuilder(dependency: component.dependency)
return LoginRouter(
interactor: interactor,
viewController: viewController,
createAccountBuilder: createAccountBuilder
)
}
}
注意,我们将 component.dependency
用作 createAccountBuilder
依赖项。为此,我们需要 LoginDependency
来实现 CreateAccountDependency
协议。这是我们将依赖关系从父 RIB
连接到子 RIB
的方式。
protocol LoginDependency: CreateAccountDependency
{
var webService: WebServicing { get }
}
在我们的示例中, CreateAccountDependency
没有任何变量。如果有的话,我们需要在某些时候提供它们。在根组件中创建并保留所有依赖项,然后使用此协议继承传递它们,这很方便。我们将在本文结尾处进行此操作。到目前为止,该应用程序应该编译没有任何错误。
五、LoginPresenter/LoginViewController
import RIBs
import RxSwift
import UIKit
1、LoginPresentableListener
protocol LoginPresentableListener: class
{
func didTapLogin(username: String, password: String)
func didTapCreateAccount()
}
我们只需要知道我们要在此 ViewController
上执行哪些操作即可。我们在 LoginPresentableListener
中添加两个方法。我们不会专注于 UI,但是如果您希望在实际操作中看到它,则可以继续创建一个简单的 UI。确保按钮触发正确的listener
方法。
2、LoginViewController
LoginViewController
类实现了我们之前配置的 LoginPresentable
协议(以便 Interactor
可以与 viewController
通信)。这意味着 LoginViewController
必须实现 showActivityIndicator
方法。
final class LoginViewController: UIViewController, LoginPresentable, LoginViewControllable
{
weak var listener: LoginPresentableListener?
// MARK: - LoginPresentable
func showActivityIndicator(_ isLoading: Bool)
{
}
}
viewController
实现的下一个协议是 LoginViewControllable
(以便 Router
可以修改视图层次结构)。为了符合要求, LoginViewController
必须实现当前方法。这是我们在 LoginViewController
中需要做的所有事情。同样,您可以添加缺少的 UI 按钮,文本字段和活动指示器。
final class LoginViewController: UIViewController, LoginPresentable, LoginViewControllable
{
weak var listener: LoginPresentableListener?
// MARK: - LoginPresentable
func showActivityIndicator(_ isLoading: Bool)
{
}
// MARK: - LoginViewControllable
func present(_ viewController: ViewControllable)
{
present(viewController.uiviewController, completion: nil)
}
}
3、LoginInteractor
因为我们向 LoginPresentableListener
添加了一些方法,并且 LoginInteractor
实现了此协议,所以我们需要向 Interactor
添加缺少的方法。
final class LoginInteractor: PresentableInteractor<LoginPresentable>, LoginInteractable, LoginPresentableListener
{
// ...
// MARK: - LoginPresentableListener
func didTapLogin(username: String, password: String)
{
}
func didTapCreateAccount()
{
}
}
didTapCreateAccount
必须路由到 CreateAccount RIB
,因此我们只需要调用现有的 LoginRouting
方法。
func didTapCreateAccount()
{
router?.routeToCreateAccount()
}
要调用登录Web
任务,我们需要访问我们之前创建的 WebServicing
登录方法。我们将把 WebServicing
传递给 LoginInteractor
构造器。
final class LoginInteractor: PresentableInteractor<LoginPresentable>, LoginInteractable, LoginPresentableListener
{
// ...
private let webService: WebServicing
init(presenter: LoginPresentable, webService: WebServicing)
{
self.webService = webService
super.init(presenter: presenter)
presenter.listener = self
}
// ...
}
在 Interator
中有 WebServicing
,我们可以完成登录方法。在此方法内部,我们实现了所有登录业务逻辑,显示和隐藏活动指示器,在登录成功时关闭 LoginFlow
页面,并在登录失败的情况下记录错误。
func didTapLogin(username: String, password: String)
{
presenter.showActivityIndicator(true)
webService.login(userName: username, password: password)
{ [weak self] result in
self?.presenter.showActivityIndicator(false)
switch result
{
case let .success(userID):
// do something with userID if needed
self?.listener?.dismissLoginFlow()
case let .failure(error):
// log error
}
}
}
我们还添加另一个 LoginPresentable
方法 showErrorAlert
,如果登录失败,该方法将通知用户。
protocol LoginPresentable: Presentable
{
var listener: LoginPresentableListener? { get set }
func showActivityIndicator(_ isLoading: Bool)
func showErrorAlert()
}
编译器将确保您已在 LoginViewController
中实现此方法。在 login
失败的情况下调用此方法。
webService.login(userName: username, password: password)
{ [weak self] result in
self?.presenter.showActivityIndicator(false)
switch result
{
case let .success(userID):
// do something with userID if needed
self?.listener?.dismissLoginFlow()
case let .failure(error):
// log error
self?.presenter.showErrorAlert()
}
}
最后,我们必须更新 LoginBuilder
并将 WebServicing
依赖项传递到 LoginInteractor
中。
final class LoginBuilder: Builder<LoginDependency>, LoginBuildable
{
override init(dependency: LoginDependency)
{
super.init(dependency: dependency)
}
func build(withListener listener: LoginListener) -> LoginRouting
{
let component = LoginComponent(dependency: dependency)
let viewController = LoginViewController()
let interactor = LoginInteractor(presenter: viewController, webService: component.dependency.webService)
interactor.listener = listener
let createAccountBuilder = CreateAccountBuilder(dependency: component.dependency)
return LoginRouter(
interactor: interactor,
viewController: viewController,
createAccountBuilder: createAccountBuilder
)
}
}
六、Root RIB
现在,我们为应用程序提供了完整的登录模块。如果您想查看全部内容,则必须添加一些缺失的部分。创建一个 Root RIB
,它将成为 Login RIB
的父级(您应该能够使用上面提供的相同步骤将登录连接到 root
。主要的区别是在RootRouter
和RootBuilder
中,因为它是一个顶级 RIB
,所以没有父 RIB
)。
1、RootBuilder
protocol RootDependency: Dependency
{
}
final class RootComponent: Component<RootDependency>
{
private let rootViewController: RootViewController
init(dependency: RootDependency,
rootViewController: RootViewController)
{
self.rootViewController = rootViewController
super.init(dependency: dependency)
}
}
除了创建 RootRouting
,我们还需要创建 LaunchRouting
(为顶级 RIB
设计的特定 RIB
组件)。
protocol RootBuildable: Buildable
{
func build() -> LaunchRouting
}
final class RootBuilder: Builder<RootDependency>, RootBuildable
{
override init(dependency: RootDependency)
{
super.init(dependency: dependency)
}
func build() -> LaunchRouting
{
let viewController = RootViewController()
let component = RootComponent(
dependency: dependency,
rootViewController: viewController
)
let interactor = RootInteractor(presenter: viewController)
return RootRouter(
interactor: interactor,
viewController: viewController
)
}
}
2、RootRouter
RootRouter
还将继承自 LaunchRouter
而不是 ViewableRouter
, 前者是特定于启动的路由协议。
final class RootRouter: LaunchRouter<RootInteractable, RootViewControllable>, RootRouting
{
override init(interactor: RootInteractable, viewController: RootViewControllable)
{
super.init(interactor: interactor, viewController: viewController)
interactor.router = self
}
}
3、AppComponent
我们还需要创建一个 AppComponent
,它继承自具有 EmptyDependency
泛型类型的 Component
。该组件有我们希望使用依赖协议传递的大多数依赖。您可以创建一个继承自 WebServicing
协议的WebService
类,并将其保留为 AppComponent
中的变量。
final class AppComponent: Component<EmptyDependency>, RootDependency
{
...
}
在 AppDelegate
中,我们需要使用此 AppComponent
创建一个 RootRouter
,并在当前窗口中启动它。
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
var window: UIWindow?
private var launchRouter: LaunchRouting?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
let window = UIWindow(frame: UIScreen.main.bounds)
self.window = window
let launchRouter = RootBuilder(dependency: AppComponent()).build()
self.launchRouter = launchRouter
launchRouter.launch(from: window)
return true
}
}
Demo
Demo在我的Github上,欢迎下载。
IOSAdvancedDemo
推荐Demo
RIBsTutorialExample
网友评论