在 TCA(The Composable Architecture)篇中,我们介绍了单向数据流。事实上,中国内地之外的韩国 iOS 圈早已有一个更早将单向架构建立为标准的框架,那就是 ReactorKit。
这个由全秀烈(Suyeol Jeon)于 2017 年公开的框架,在 StyleShare 和 Kakao 系列服务采用后,成为 UIKit + RxSwift 组合的事实标准架构。甚至曾有一段时间,招聘信息中经常出现“有 ReactorKit 经验者优先”。
今天我们来梳理 ReactorKit 的结构、它与 TCA 的区别,以及它当前的定位。
View 和 Reactor,只有两个角色
ReactorKit 的结构很简单。每个页面都拆分为 View 和 Reactor。
- View:将用户输入流向
Action,并订阅State来绘制页面。不包含任何逻辑。 - Reactor:接收 Action 并生成 State 的逻辑模块。它完全不了解 UI。
关键在于,Reactor 内部的数据流被固定为单一方向。
Action → mutate() → Mutation → reduce() → State → View
按钮点击以Action.refresh进入后,mutate()会处理网络请求等副作用,并发出Mutation.setItems([...])。reduce()接收这个 Mutation 后计算新的 State。View 订阅 State,然后更新页面。
为什么需要 Mutation 这个中间阶段
如果你熟悉 MVVM(Model-View-ViewModel),可能会问:“为什么不直接从 Action 修改 State?”加入 Mutation 是为了隔离异步处理。
mutate()是唯一允许执行异步工作的地方。相反,reduce()是纯函数,因此输入相同的 State 和 Mutation 时,总会得到相同结果。副作用被固定在一个位置,所以追踪状态为何变化时,也只需要查看一个地方。
final class SearchReactor: Reactor {
enum Action { case updateQuery(String) }
enum Mutation { case setResults([Repo]) }
struct State { var results: [Repo] = [] }
func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .updateQuery(let query):
return service.search(query)
.map { Mutation.setResults($0) }
}
}
func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case .setResults(let repos): newState.results = repos
}
return newState
}
}
Reactor 是不了解 UI 的纯逻辑,因此测试也很简单。传入 Action,再检查 State 即可。
它与 TCA 有什么不同
两者都是受 Redux 影响的单向架构,但应用单位不同。
| ReactorKit | TCA | |
|---|---|---|
| 基础 | RxSwift | Combine·Swift Concurrency |
| 应用单位 | 每个页面(View)一个 Reactor | 将功能级 Reducer 合成为树 |
| 引入方式 | 可以按页面逐步引入 | 将整个应用构建为一个体系 |
| 主要场景 | UIKit | SwiftUI |
ReactorKit 的实用性来自它的引入方式。在现有 MVC(Model-View-Controller)项目中,只需选定一个页面并添加 Reactor 即可运行,不需要重写整个应用。相比之下,TCA 还会管理功能之间的状态组合,但需要整体采用这套体系。
另外,还有一个将 Redux 直接移植到 iOS 的库 ReSwift,但应用全局单一 store 的方式难以与 UIKit 配合,因此没有像 ReactorKit 那样普及。
当前的 ReactorKit
坦率地说,新项目选择 ReactorKit 的情况正在减少,因为它的弱点已经变得明显。
第一,它与 RxSwift 强耦合。随着 Apple 生态转向 Combine 和 Swift Concurrency,RxSwift 依赖本身成了负担。
第二,它与 SwiftUI 不匹配。View 协议和绑定方式都是以 UIKit 的命令式更新为前提设计的。
不过,ReactorKit 留下的遗产很明确。这个框架最早向韩国 iOS 开发者传递了“状态只沿一个方向流动”和“副作用隔离在一个地方”的理念。如果你现在正在学习 TCA,Action·Mutation·State 结构应该不会陌生;这种直觉很大一部分是在 ReactorKit 时代形成的。
如果你正在维护现有的 UIKit + RxSwift 代码库,ReactorKit 仍然是经过验证的选择。由于它按页面接入和移除,之后也可以逐个页面拆除并迁移,相对比较容易。
总结
- ReactorKit 是基于 RxSwift 的架构,将一个页面拆分为 View 和 Reactor,并强制采用 Action → Mutation → State 的单向流程。
- 异步处理隔离在
mutate()中,纯状态计算隔离在reduce()中。因此,追踪和测试都很容易。 - 它与 TCA 的概念相同,但按页面逐步引入的实用路线是其差异所在。
- 由于与 RxSwift 耦合且不兼容 SwiftUI,新项目中的引入有所减少,但在 UIKit 遗留代码中仍然有效。
下一篇将介绍 Uber 的 RIBs,它不是按页面,而是按业务逻辑单元拆分应用。我们会深入分析 VIPER 篇中只用一段提到的那个架构。

![[iOS 架构 #9] ReactorKit 总结:单向架构的标准 封面图](/assets/images/posts/317cfd0d-2a59-49f9-9bce-a85ec25ae593/reactorkit-unidirectional-flow-1.jpg)