如何在 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
但这就是我的看法。
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
但这就是我的看法。