在 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 合成樹狀結構 |
| 導入方式 | 可逐一畫面漸進式導入 | 將整個 App 建構為單一體系 |
| 主要場景 | UIKit | SwiftUI |
ReactorKit 的實用性來自導入方式。在既有 MVC(Model-View-Controller)專案中,只要挑選一個畫面並加上 Reactor 就能運作,不需要重寫整個 App。相較之下,TCA 會連功能之間的狀態合成一起管理,但必須採用整套體系。
另外也曾有將 Redux 直接移植到 iOS 的 ReSwift 函式庫,但 App 全域單一儲存庫的方式難以與 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,探討如何以商業邏輯而非畫面單位拆分 App。我們會深入分析 VIPER 篇中只用一段提到的那個架構。

![[iOS 架構 #9] ReactorKit 總結:單向架構的標準 封面圖](/assets/images/posts/317cfd0d-2a59-49f9-9bce-a85ec25ae593/reactorkit-unidirectional-flow-1.jpg)