如何在 VIPER 设计模式中传递 ViewController 对路由器的引用?

How to pass ViewController Reference to Router in VIPER design pattern?

P.S: 这不是一个自以为是的问题。在 VIPER 中连接各种模块是一个合理的怀疑。这是一个理论问题,因此没有附加代码。我只需要知道我们如何在这种特定情况下连接 View-Presenter-Router 而不会违反 VIPER

的基本规则

我第一次尝试使用 VIPER。这是我对 VIPER 的基本理解。

视图: 应该显示 UI 控件并捕获 IBActions 并调用其演示者的委托方法来处理事件

Presenter: 将处理所有UI相关数据并准备渲染数据,并将数据交还给View。每当需要屏幕转换时,它都会调用其路由器并要求路由器执行转换

P.S: Presenter 中将没有任何 UIComponents。所以演示者中没有 import UIKit 语句。

路由器: 负责执行屏幕转换,通常借助线框(可选但最好在应用程序中有这样的 class)

Interactor:包含所有业务logic.Presenter只要需要根据业务逻辑处理就会调用Interactor

实体: POJO classes(简单Swift对象或核心数据实体)。

问题来了:

如果我的假设是正确的,Presenter 应该是没有 UIKit 访问权限的普通 Swift class。

如果为真,假设我在我的 ViewControllerA 上按下一个按钮,我需要在它上面按下另一个 ViewControllerB,显然 ViewControllerA 会与 PresenterA 对话并告诉它按钮被点击,现在 PresenterA 应该与 RouterA 对话并告诉它按下 ViewControllerB

因为路由器可以访问 UIKit 我可以使用故事板实例或从 xib 轻松创建 ViewControllerB 的新实例,但是为了推送该实例我需要 ViewControllerA的实例。

但是 PresenterA 不能保存对 ViewControllerA 的引用,或者可以在函数中作为参数传递给 PresenterA 因为 UIViewController 属于 UIKit 并且演示者不应该有 UI 陈述。

我能想到的可能的解决方案:

方案一:

在创建 Router 实例时,将相应的 ViewController 实例作为其初始化(依赖注入阶段)的一部分传递,这样 Router 将始终引用 ViewController 它属于

方案二:

让 Router 声明其协议并在 ViewController 中实现它,只要引用 ViewController 需要使用 Router 的委托。但这与路由器不应该与 View 通信的 VIPER 规则相矛盾。

我的想法是正确的吗?我的假设是否正确?如果是,处理这个问题的正确方法是什么,请建议

关于 iOS 应用程序的 VIPER 的任何建议或意见都值得商榷,因为 VIPER 并不严格适合 iOS 的 UIKit 设计。但是,如果我可以把我的两分钱放在讨论中:

首先,我认为 UIViewController 完全适合 VIPER 模式中 Presenter 的角色,因此 ViewControllerA 不需要与任何 class 在自身和 Router 之间 - 直接与 Router 通信。

其次,应该只有一个 Router 对象 - 因为应用程序只有一个 view/navigation 堆栈。出于这个原因,为 Router 实施 Singleton 模式是理想的,因此 ViewControllerA(或 Presenter,如果您更愿意让 ViewControllers 执行此角色该模式中的 View)不需要保留对 Router.

的引用

第三,您不需要将对 ViewControllerA 的引用传递给您的 Router - Router 应该已经有了对它的引用,因为 Router 是应该首先展示它。类似的东西:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
{
    // ...

    window?.rootViewController = Router.shared.rootViewController

    // ...
}

class Router
{
    static let shared = Router()

    let rootViewController = ViewControllerA() // or UINavigationController, or UITabBarController etc.
}

Router 应跟踪导航堆栈并参考当前呈现的内容 ViewController

但这就是我的看法。