在 VIPER(View·Interactor·Presenter·Entity·Router)篇中,我曾用一段话带过“Uber 受 VIPER 启发创建了 RIBs”,这次将详细介绍。在韩国,RIBs 也因 Toss 采用了这种架构而广为人知。
理解 RIBs 的关键只有一个。此前介绍的所有架构(MVC·MVVM·VIPER·ReactorKit)都以屏幕为基本单元。RIBs 放弃了这一前提,改为按业务逻辑单元拆分应用,而不是按屏幕拆分。
无屏幕组件的存在
RIBs 是 Router·Interactor·Builder 的缩写。这三者是每个组件(RIB)的必需部分,只有需要时才会加入 Presenter 和 View。
- Interactor:业务逻辑的核心,也是 RIB 的大脑。
- Router:通过 attach 和 detach 子 RIB 来管理树。
- Builder:组装一个 RIB,并注入依赖。
- Presenter·View:可选项,只存在于需要屏幕的 RIB 中。
“View 是可选项”这一点至关重要。以叫车应用的“乘车中”状态为例,它涵盖地图屏幕、司机信息卡和支付准备逻辑,但本身并不是某一个具体屏幕。在 RIBs 中,可以将其做成无屏幕的 viewless RIB,再在其下连接拥有屏幕的子 RIB。
整个应用会变成一棵 RIB 树。登录状态、乘车状态等应用状态的变化,就是树形结构的变化。退出登录时,LoggedIn RIB 会从树中整体移除,其下的屏幕和逻辑也会一起消失。状态管理就是树管理。
为什么只有超大型组织会使用?
RIBs 从一开始的设计目标就是“数百人共同开发一个应用”。Uber 应用中有数十个团队同时提交代码,仅按屏幕拆分无法建立团队边界,因为一个屏幕中会混入多个团队的逻辑。
按 RIB 拆分后,每个团队都能拥有自己的 RIB 子树。Builder 强制规定依赖注入点,因此根本无法接触其他团队 RIB 的内部。这是在架构层面实现模块化系列中提到的“强制边界”。
Toss 采用 RIBs 的原因也相同。在一个应用包含数十种金融服务的超级应用结构中,让每项服务成为一棵 RIB 子树后,连接和移除都会更加明确。
反过来,这些优点在小型团队中都会变成成本。
第一,RIBs 的样板代码比 VIPER 还多。一个 RIB 通常会产生四五个文件。因此 Uber 也提供了代码生成模板。
第二,学习曲线很陡。团队必须掌握树设计、attach/detach 时机以及按作用域进行依赖注入,才能形成统一的理解。
第三,它与以屏幕为中心的思维方式不一致。产品需求通常按屏幕编写,而 RIBs 按状态拆分,因此设计阶段需要进行转换。
它与 VIPER 有什么不同?
只看组件名称,RIBs 像是 VIPER 的变体,但两者的差异是根本性的。
| VIPER | RIBs | |
|---|---|---|
| 基本单元 | 屏幕 | 业务逻辑 |
| 无屏幕组件 | 不可能 | viewless RIB |
| 导航 | Router 负责屏幕切换 | Router 负责树的 attach/detach |
| 目标 | 分离一个屏幕的职责 | 按组织单元分离代码所有权 |
VIPER 的 Router 负责“如何进入下一个屏幕”,而 RIBs 的 Router 负责“在当前应用状态下,哪些逻辑组件应该存活”。名称相同,问题不同。
总结
- RIBs 是 Uber 的架构,用业务逻辑单元而不是屏幕将应用拆成树形结构。
- RIB 可以没有屏幕,并通过树的 attach/detach 表示应用状态变化。
- 它的目标是按团队划分代码所有权并强制边界,因此在数百人规模的组织中最能发挥价值。Uber 和 Toss 是代表案例。
- 对于小型团队,样板代码和学习曲线会压过这些优点。正如第 8 篇选择指南所述,团队规模是大部分答案。
至此,iOS 架构系列中只提到但未展开的部分也全部补齐了。从 MVC 到 RIBs,请记住所有架构最终都是对同一个问题“逻辑应该放在哪里”的不同回答。

![[iOS 架构 #10] Uber RIBs:按业务逻辑拆分应用 封面图](/assets/images/posts/f06929f4-621d-40e7-88d9-8817b02ef56d/uber-ribs-architecture-hero-1.jpg)