Swift下VIPER架构跨模块调用方法及传数据的正确实现方式
VIPER架构下返回根视图并触发根模块方法的实现方案
VIPER架构的核心要求是各模块职责单一、通过约定接口通信,禁止跨模块直接获取私有成员(比如直接取根视图控制器调用方法),避免模块间强耦合。具体实现可以按照以下步骤操作:
实现逻辑
整个流程完全遵循VIPER的分层调用规则:
- 交互从模块3的View层发起,传递给模块3的Presenter
- 模块3的Presenter通知自身Router执行全局跳转逻辑
- Router持有导航控制器引用,执行popToRoot的同时,通过模块1的对外公开接口调用目标方法,不直接依赖模块1的ViewController实现
具体代码实现
第一步:定义模块1的对外业务接口
模块1的能力需要先通过协议对外暴露,约定方法签名,由模块1的Presenter遵循实现:protocol Module1ExternalInterface: AnyObject { func someFunc() } // 模块1的Presenter遵循该协议,实现业务逻辑 final class Module1Presenter: Module1ExternalInterface { func someFunc() { // 原有业务逻辑实现 } }第二步:模块3发起返回请求
模块3内触发返回根视图的交互后,按照分层规则向上传递指令:// 模块3 ViewController层 func didTriggerBackToRootAction() { output?.needPopToRootAndUpdateRootModule() } // 模块3 Presenter层 func needPopToRootAndUpdateRootModule() { router?.executePopToRootWithModule1Update() }第三步:Router执行跳转并调用模块1方法
Router作为唯一负责跳转的组件,直接通过导航控制器获取根模块的对外接口实例,调用目标方法:// Router层实现 func executePopToRootWithModule1Update() { guard let nav = navigationController, let rootVC = nav.viewControllers.first as? Module1ViewInput, let module1Presenter = rootVC.output as? Module1ExternalInterface else { return } // 执行返回根视图动画 nav.popToRootViewController(animated: true) // 通过公开接口调用模块1方法 module1Presenter.someFunc() }如果希望进一步降低耦合,也可以在初始化根模块的时候,将模块1的
Module1ExternalInterface实例注入到全局Router中存储,调用时直接使用存储的实例即可,不需要依赖具体的ViewController类型。
可选替代方案:通知中心
如果两个模块没有直接的跳转关联,也可以通过通知中心实现松耦合通信:
- 模块1的Presenter初始化时监听自定义通知,收到通知后执行
someFunc() - Router执行完
popToRoot后发送对应通知即可
该方案适合跨多个无关联模块的通信,缺点是调用链路不透明,优先使用前述接口方案更符合VIPER的设计规范。
内容的提问来源于stack exchange,提问作者Vyachaslav Gerchicov
相关产品推荐
相关产品推荐

