軟體設計

何時導入 DI 容器?Swinject・Factory 導入時機總整理

進行 iOS 開發時,總有一天會浮現這個問題。

閱讀 4 分鐘
何時導入 DI 容器?Swinject・Factory 導入時機總整理 封面圖

進行 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 容器則是在單一位置管理這些物件要在哪裡、如何建立的工具。

如果只靠建構子注入就足夠,現在導入容器還太早。

我會用以下幾點判斷導入訊號。

  1. 初始化程式碼在每個畫面重複,開始厭倦傳遞物件時
  2. 在測試中經常要替換成假的物件時
  3. App 全域需要讓多個地方共用同一個執行個體(具單例性質)時
  4. 團隊變大,經常問「這個物件在哪裡建立?」時

如果符合其中兩項以上,就可以開始考慮導入容器。


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 一個就夠了

無論是個人或團隊專案,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。它是為補足既有容器的缺點而生,無論小規模開始或擴大使用都很完整。先數數看前面提到的四個導入訊號,目前符合幾項。