Swifにおける依存性の注入(DI)について Swinjectを使って紐解く

この記事では、Swiftにおける依存性の注入についてをまとめていきます。

Dependency Injection

Dependency Injectionを直訳されたのが依存性の注入です。 正直、依存性の注入と言われてもさっぱり意味がわかりません。 そこで、調べて見ることにしました。 Dependencyは、依存性と訳されることが多いですが、Objectと考える方がわかりやすい とあります。 確かに、objectを注入すると考えると分かりやすいかもしれません。 必要なobjectを毎回初期化して利用するのではなく、 1つの場所でObjectを初期化しておき必要な時はそこからObjectを受け取るというイメージです。 今回利用するフレームワークのSwinject以外にもsharedを使って行うことがあります。

DIを行うことで、テストが簡単に書くことが出来るなどのメリットがあります。

では、実際のコードを見ながらDIについて考えたいと思います。 ここでは、 DIを行うフレームワークのSwinjectを使ってみました。

//ViewControllerAssembly
import UIKit
import Swinject

final class ViewControllerAssembly: Assembly {
    func assemble(container: Container) {

        //MARK: -ReaderViewController

        container.register(ReaderViewController.self) { resolver in
            let viewController = UIStoryboard.instantiateViewController(of: ReaderViewController.self)
            viewController.viewModel = resolver.resolve(ReaderViewModel.self)!
            return viewController
        }
    }
}

containerは、複数個のオブジェクトを管理するものです。DIで実装していくと引数が多くなるため同じオブジェクトをまた生成するというコストを削減させることができる。 そのcontainerに、container.registerでViewControllerに登録して行きます。 instantiateViewControllerはstoryboardからViewControllerを作成する方法です。Swinjectでは、Swinjectstoryboardというものもあるのでそちらを利用するのもおすすめです。 そして、下記のコードでViewModelをcontainerに登録し、それをresolver.resolveで呼んでいます。 最後にreturnで値を返しています。

//ViewModelAssembly
import Swinject

final class ViewModelAssembly: Assembly {
    func assemble(container: Container) {

        //MARK: - ReaderViewModel

        container.register(ReaderViewModel.self) { (resolver) in
            return ReaderViewModel()
        }
    }
}
//Container+Extension
import Swinject

extension Container {
    static let shared = assembler.resolver
    
    private static let assembler = Assembler([
        ViewControllerAssembly(),
        ViewModelAssembly(),
        ])
}

Container+Extensionでは、 ViewControllerAssembly( ) ,ViewModelAssembly( )の二つをShredで呼べるようにしています。これを行うことで2つのassemblyをシングルトンとして利用することができます。

//UIViewController.Container+Extension
import Swinject
import UIKit

extension ReaderViewController {
    static func make() -> ReaderViewController {
        return Container.shared.resolve(ReaderViewController.self)!
    }
}

このmake( )を使うことで、Swinjectを使ってオブジェクトを外部から注入することができます。 実際にAppDelegateでwindow?.rootViewController = ReaderViewController.make()でクラスの初期化を行なっています。