11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Swift UI + SwinjectでDI(Dependency Injection)する方法

Posted at

SwiftUIでSwinjectなどのDIコンテナを使ってDIするときの解決策として、ネストした型を使う方法を考えてみたので共有します。

SwiftUIの場合、どこでDIする?

SwinjectでDIするとき、従来のStoryboardならSwinjectStoryboardというものが用意されていました。SwiftUIの場合はどうしたらよいでしょうか?

例えば、次のコードのSomeContentViewをDIしたいときはどうすればよいか?

'SwiftUIの例
struct BookMarkRow: View {
    var body: some View {
        HStack {
            Text(bookMark.title)
            NavigationLink(destination: SomeContentView()) {
                Text("Next Page")
            }
        }
    }
}

解決策

まず、 DependencyInjectableというDI可能なprotocolを宣言します。

DIできるprotocolの宣言
protocol DependencyInjectable {
    associatedtype DependencyType
    var di: DependencyType! {get set}
    func resolveDependencyInstance() -> DependencyType
}

extension DependencyInjectable {
    internal func resolveDependencyInstance() -> DependencyType {
        let container = DIContainer.shared.getContainer()

        return container.resolve(type(of: self.di))!!
    }
}

次に、これを実装したstructを作ります。
struct Dependencyの中に依存するオブジェクトが注入されます。

DIできる型を実装したstruct
struct SomeContentView: DependencyInjectable {
    typealias DependencyType = Dependency
    struct Dependency {
        var p: TestProtocol
    }
    var di: Dependency!
    
    init() {
        di = resolveDependencyInstance()
    }
    
    func use() {
        di.p.test()
    }
}

依存性を解決するDIコンテナを宣言します。
SomeProtocolSomeProtocolImplは何でも良いので省略します

DIコンテナの宣言
class DIContainer {
    static let shared = DIContainer()

    private init () {}

    func getContainer() -> Container {
        let container = Container()

        container.register(SomeProtocol.self) { r in
                return SomeProtocolImpl()
        }

        container.register(SomeContentView.DependencyType.self) { r in
                return SomeContentView.DependencyType(p: container.resolve(SomeProtocol.self)!)
        }
        
        return container
    }
}

実際に使うときは次のようにすれば、SomeContentViewは依存性が注入された形でインスタンスが生成されます。

使い方
let c = SomeContentView()
c.use()

まとめ

DependencyTypeの中に依存するオブジェクトを宣言しておけば、あとはDIコンテナ内で依存関係を解決してやれば良いので、手間いらずだと思います。

extensionの中にDIコンテナが入るので、Service Locatorのようにも見えますが、依存するものはすべてDependencyTypeの実装の中で確定し、依存するものはすべてDIコンテナの中で生成しているので、Service Locatorのように依存関係がわかりづらくなることはないと思います。

ほかに、もっと良い方法があれば教えて下さい。

11
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?