Skip to content

Auto injection

Ilya Puchka edited this page Dec 10, 2016 · 4 revisions

Auto-injection lets your resolve all property dependencies of the instance resolved by container with just one call, also allowing a simpler syntax to register circular dependencies.

protocol Server {
    weak var client: Client? { get }
}

protocol Client: class {
    var server: Server? { get }
}

class ServerImp: Server {
    private let injectedClient = InjectedWeak<Client>()
    var client: Client? { return injectedClient.value }
}

class ClientImp: Client {
    private let injectedServer = Injected<Server>()
    var server: Server? { get { return injectedServer.value} }
}

container.register(.Shared) { ServerImp() as Server }
container.register(.Shared) { ClientImp() as Client }

let client = try! container.resolve() as Client

Auto-Injected properties are required by default, so if container fails to resolve one of them it will fail to resolve the whole object graph. You can make it optional providing false value for required parameter in Injected or InjectedWeak constructor.

Auto-injection is performed before calling resolveDependencies block of corresponding definition.

Tip: You can use either Injected<T> and InjectedWeak<T> wrappers provided by Dip, or your own wrappers (even plain Box<T>) that conform to AutoInjectedPropertyBox protocol. This way you can minimise coupling with Dip.

Note on named definitions

If you try to resolve type that uses auto-injected properties with named definition the tag that you use to resolve will be implicitly used to resolve all the properties. That can lead to unexpected not shared dependencies in the graph.

class ServiceImp: Service {
  let config = Injected<Config>()
  let dataStore = Injected<DataStore>()
}
container.register() { ServiceImp() as Service }
container.register(tag: "dev") { DevConfig() as Config }
container.register(tag: "prod") { ProdConfig() as Config }
container.register(.singleton) { DataStore() }

let devService = try! container.resolve(tag: "dev") as Service
let prodService = try! container.resolve(tag: "prod") as Service

devService.config.value !== prodService.config.value //expected
devService.dataStore.value !== prodService.dataStore.value //not expected

To avoid that specify tag in injected property explicitly. You can use nil to make container to not use any tag or some specific tag value.

class ServiceImp: Service {
  let config = Injected<Config>()
  let dataStore = Injected<DataStore>(tag: nil)
}

let devService = try! container.resolve(tag: "dev") as Service
let prodService = try! container.resolve(tag: "prod") as Service

devService.config.value !== prodService.config.value //still different instances as expected
devService.dataStore.value === prodService.dataStore.value //shared instance now