Skip to content

Auto wiring

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

When you use constructor injection to inject dependencies of your component and they are also registered in the container auto-wiring enables you to resolve these components just with one call to resolve method without providing its dependencies as arguments or without explicitly resolving them inside the definition's factory. Instead container will resolve them for you.

class PresenterImp: Presenter {
    init(view: ViewOutput, interactor: Interactor, router: Router) { ... }
    ...
}

container.register { RouterImp() as Router }
container.register { View() as ViewOutput }
container.register { InteractorImp() as Interactor }
container.register { PresenterImp(view: $0, interactor: $1, router: $2) as Presenter }

let presenter = try! container.resolve() as Presenter

Auto-wiring uses Swift Type Inference to infer types of constructor arguments and if they all are registered in container, they will be automatically resolved.

Note: it also can handle optional dependencies

At the same time you are able to resolve the component using runtime arguments:

let view = try! container.resolve() as ViewOutput
let interactor = try! container.resolve() as Interactor
let router = try! container.resolve() as Router

//without auto-wiring
let presenter = try! container.resolve(withArguments: view, interactor, router) as Presenter

Auto-wiring saves all these calls to resolve each constructor argument.

Alternatively you could register factory that does not accept runtime arguments but explicitly resolves constructor arguments:

container.register { 
    try PresenterImp(
        view: container.resolve(), 
        interactor: container.resolve(),
        router: container.resolve()
    ) as Presenter 
}

In this case auto-wiring again lets you avoid this repetitive calls to resolve method inside the factory.

Note on named definitions

If you try to resolve type using auto-wiring and named definition the tag that you use to resolve will be implicitly used to resolve all the constructor dependencies. That can lead to unexpected not shared dependencies in the graph.

container.register() { ServiceImp(
  config: $0 as Config, 
  dataStore: $1 as DataStore) 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 !== prodService.config //expected
devService.dataStore !== prodService.dataStore //not expected

To avoid that resolve these dependencies explicitly:

container.register() { try ServiceImp(
  config: $0 as Config, 
  dataStore: container.resolve() as DataStore) 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 !== prodService.config //still different instances as expected
devService.dataStore === prodService.dataStore //shared instance now