美文网首页
IOS架构:RIBS

IOS架构:RIBS

作者: 时光啊混蛋_97boy | 来源:发表于2021-05-24 15:32 被阅读0次

    原创:知识探索型文章
    创作不易,请珍惜,之后会持续更新,不断完善
    个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的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(肋骨)被设计成这样,那和之前的VIPERMVC有什么区别呢?最大的区别在路由上面。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。展示器Presentermodel转换成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 解决(通过Falconinstall-xcode-template.sh)。

    要创建新的 RIB ,只需打开文件创建菜单,然后从列表中选择 RIB

    我们将创建一个名为 LoginRIB ,并勾选 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 创建一个 LoginComponentLoginComponent 现在封装了这个 RIB 需要的所有依赖项。

    该构建器还创建一个 LoginViewControllerLoginInteractor,用于创建和返回 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 实例。这是 LoginViewControllerInteractor 进行通信并调用业务逻辑的一种方式。换句话说,这是 InteractorViewController 相互通信的方式。

    如上所述,我们希望我们的视图控制器在执行 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 变量: routerlistener。这就是 Interactor 连接到其 Router 和父 Interactor 的方式。可以看到,该Interactor还拥有一个 Presenter

    接下来,我们可以看到一些生命周期方法 didBecomeActivewillResignActive 。这些方法是自解释的,我们不会直接调用它们。例如,我们可以在 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 是这里的主要协议,包含两个组件, LoginRoutingLoginListener。我们在 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 RIBLoginRouter 需要有一个 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。主要的区别是在RootRouterRootBuilder 中,因为它是一个顶级 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

    参考文献

    相关文章

      网友评论

          本文标题:IOS架构:RIBS

          本文链接:https://www.haomeiwen.com/subject/aeowjltx.html