LoginSignup
12
9

More than 1 year has passed since last update.

Swinjectにおけるインスタンスのスコープに注意

Last updated at Posted at 2018-04-04

SwiftのDIコンテナライブラリであるSwinjectSwinjectStoryboardをプロジェクトで使用しているのですが、少しハマったことがあったので書きます。

以下に完全なコードを載せていますが、注目してほしいのはViewModelAssemblyのところです。
ViewModelAssembly内でAppViewModelのインスタンスを登録していて、それを同じViewModelAssembly内でMainViewModelSubViewModelにインジェクトしています。

一見正しそうに見えますが、実はMainViewModelSubViewModelには別々のAppViewModelインスタンスがインジェクトされてしまいます。

SwinjectStoryboard+Setup.swift
import Swinject
import SwinjectStoryboard

extension SwinjectStoryboard {
    class ApiAssembly: Assembly {
        func assemble(container: Container) {
            container.register(API.self) { _ in
                return API()
            }
        }
    }

    class ViewModelAssembly: Assembly {
        func assemble(container: Container) {
            container.register(AppViewModelProtocol.self) { _ in
                return AppViewModel()
            }

            container.register(MainViewModelProtocol.self) { r in
                let api = r.resolve(API.self)!
                let appViewModel = r.resolve(AppViewModelProtocol.self)!
                return MainViewModel(api: api, appViewModel: appViewModel)
            }

            container.register(SubViewModelProtocol.self) { r in
                let api = r.resolve(API.self)!
                let appViewModel = r.resolve(AppViewModelProtocol.self)!
                return SubViewModel(api: api, appViewModel: appViewModel)
            }
        }
    }

    class ViewControllerAssembly: Assembly {
        func assemble(container: Container) {
            container.storyboardInitCompleted(MainViewController.self) { r, c in
                c.vm = r.resolve(MainViewModelProtocol.self)!
            }
        }

        func assemble(container: Container) {
            container.storyboardInitCompleted(SubViewController.self) { r, c in
                c.vm = r.resolve(SubViewModelProtocol.self)!
            }
        }
    }

    @objc class func setup() {
        let assembler = Assembler(container: SwinjectStoryboard.defaultContainer)
        assembler.apply(assemblies: [
            ApiAssembly(),
            ViewModelAssembly(),
            ViewControllerAssembly()
        ])
    }
}

SwinjectではOpjectScopeという概念があり、デフォルトでは型が毎回resolveされるたびに、新しいインスタンスが作成されるようになっています。

インスタンスの作成は一度だけにして、二回目以降にresolveされる際は既存のインスタンスを返すようにするには、inObjectScope()メソッドを呼んであげる必要があります。

SwinjectStoryboard+Setup.swift
    class ViewModelAssembly: Assembly {
        func assemble(container: Container) {
            container.register(AppViewModelProtocol.self) { _ in
                return AppViewModel()
            }
            .inObjectScope(.container)  // スコープを指定する!!
        }
    }
12
9
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
12
9