進行 iOS 開發時,總有一天會浮現這個問題。
「這個直接透過建構子傳入就好,真的需要特別使用 DI 容器嗎?」
隨著 side project 變大,這是每個人都會遇到一次的煩惱。我實際在專案中使用過 Swinject 和 Factory,建立了判斷標準。
先說結論。
如果畫面少於 20 個,只靠建構子注入、不使用 DI 容器就已經足夠。
當相依性圖變複雜,測試替身經常需要更換時,就是導入時機。
如果要導入,選 Factory。
我會按照自己的經歷,說明何時導入,以及為什麼選 Factory。
導入 DI 容器前,請先確認這些事
先整理最容易混淆的部分。
相依性注入(DI)和 DI 容器並不相同。
相依性注入是從外部提供所需物件的設計習慣,完全不需要函式庫。
// 這也已經是很好的相依性注入
final class LoginViewModel {
private let authService: AuthService
init(authService: AuthService) {
self.authService = authService
}
}
DI 容器則是在單一位置管理這些物件要在哪裡、如何建立的工具。
如果只靠建構子注入就足夠,現在導入容器還太早。
我會用以下幾點判斷導入訊號。
- 初始化程式碼在每個畫面重複,開始厭倦傳遞物件時
- 在測試中經常要替換成假的物件時
- App 全域需要讓多個地方共用同一個執行個體(具單例性質)時
- 團隊變大,經常問「這個物件在哪裡建立?」時
如果符合其中兩項以上,就可以開始考慮導入容器。
Swinject 和 Factory 有什麼不同?
Swinject 是 Swift 生態系自 2015 年起使用的傳統執行階段容器。它會註冊到 Container,再透過 resolve 取出。問題在於,這個 resolve 會回傳選擇型別。忘記註冊時,編譯仍會正常通過,直到執行 App、在該畫面強制解包 nil 時才會以當機告終。
Factory 是為了補足這類傳統容器的缺點而誕生的專案。建立 Resolver 的 Michael Long 以那段經驗重新設計了它。核心概念是「定義就是註冊」。由於工廠是以容器的屬性定義,根本不存在忘記註冊這件事。參照錯誤會在編譯時而非執行階段報錯。
以下整理我截至 2026 年感受到的差異。
| 項目 | Swinject | Factory |
|---|---|---|
| 註冊・解析方式 | 執行階段 resolve(回傳選擇型別) | 基於型別的定義+屬性包裝器 |
| 漏註冊時 | 執行階段 nil/當機 | 以編譯錯誤事先阻擋 |
| 範圍管理 | 支援 | 支援(.singleton・.cached・.shared 等) |
| 外部相依性 | 相依於其他框架 | 輕量單一套件 |
| 學習曲線 | 稍陡 | 相對平緩 |
過去有人建議「需要複雜範圍管理就用 Swinject」,但現在不是這樣。Factory 也完整支援單例、快取與共用範圍。Swinject 能做的事,Factory 能更安全地完成。
所以結論是 Factory
我實際判斷時採用的標準如下。
如果新導入,Factory 一個就夠了
無論是個人或團隊專案,Factory 都能不依賴外部相依性、從一個套件開始,因此負擔很小。最關鍵的是,因忘記註冊而在執行階段爆掉的事故,從結構上消失了。
// Factory: 定義就是註冊
extension Container {
var authService: Factory<AuthService> {
self { LiveAuthService() }.singleton // 範圍也只要一行
}
}
// 使用端
@Injected(\.authService) private var authService
在測試中替換成 mock 也只要一行。
Container.shared.authService.register { MockAuthService() }
什麼時候保留 Swinject?
維護已經以 Swinject 建立的大型程式碼庫時。不必立刻拆掉運作良好的組裝(assembly)結構。不過,即使在這類專案中,也越來越多團隊從新模組開始逐步轉換至 Factory。現在很難找到新導入時選擇 Swinject 的理由。
常見問題(Q&A)
Q. 從一開始就加入容器不行嗎?
不會阻止你,但不推薦。相依性圖簡單時,容器反而會多藏一層程式碼,讓追蹤更困難。
Q. 可以從 Swinject 轉換到 Factory 嗎?
可以。讓兩個容器暫時共存,再以模組為單位移轉,是比較實際的漸進式移轉方式。由於必須重新調整註冊位置與範圍設定,規模大時會花時間,但移轉後就不用擔心執行階段當機。
Q. SwiftUI 專案該選哪個?
當然是 Factory。它以屬性包裝器為基礎,能自然融入 SwiftUI 的宣告式風格,在預覽中插入 mock 也很簡單。
DI 容器在真正需要時導入,最俐落。
既然決定導入,就直接選 Factory。它是為補足既有容器的缺點而生,無論小規模開始或擴大使用都很完整。先數數看前面提到的四個導入訊號,目前符合幾項。

