DependencyContainer
public final class DependencyContainer : DependencyResolver
extension DependencyContainer: CustomStringConvertible
A DependencyContainer
is used to create and access instances of services that the SDK needs in a configurable way, using inversion of control (IoC).
Container
Interactions with dependencies will happen through a container. You start by creating one (or multiple) containers to register services required for booting the framework or application.
let container = DependencyContainer()
Factory
The most common method for registering dependencies is by associating a protocol or type to a factory. This will ensure a new instance of your dependency is created everytime it is resolved.
container.register { ServiceImplementation() as ServiceInterface }
It is also possible to explicitly define the type as argument.
container.register(as: ServiceInterface.self) { ServiceImplementation() }
Resolution
You can resolve a concrete instance of protocol or type using the container.
let object = try! container.make() as ServiceInterface
It is also possible to specify the type in parameter instead.
let object = try! container.make(ServiceInterface.self)
Warning
The order in which dependencies are registered is not relevant, as long as:- your dependencies resolve types that have been registered previously
- you don’t register a dependency for a type already registered
Note that overriding dependency registrations may be desired to allow custom injections replacing default types for example, but you must then carefully check the order of registrations.
Scopes
Singleton Instance
You can also register an initialized instance of a dependency, which will be reused by the container while resolving the dependency.
container.register(ServiceImplementation(), as: ServiceInterface.self)
Or specify the singleton
scope while registering a factory, which will resolve the factory only once and then reuse the same instance.
container.register(.singleton) { ServiceImplementation() as ServiceInterface }
Shared Instances
Sometimes you need to register different factories or different implementations of the same protocol.
By default, if you register a dependency for the same type multiple times, the latest definition will override a previous one. This is called the unique
scope.
To solve that, you can use shared dependencies by providing a shared
scope with a specific context to the register
method:
container.register(.shared(.myContext)) { ServiceImplementation() as ServiceInterface }
container.register(.shared(.anotherContext)) { ServiceImplementation() as ServiceInterface }
When you provide a context to resolve, container will look for a dependency associated with this context:
let object = try! container.make(ServiceInterface.self, context: .myContext)
let anotherObject = try! container.make(ServiceInterface.self, context: .anotherContext)
Dependency Provider
In order to organize dependencies in a better way, you can use multiple DependencyProvider
to register all dependencies you would like to provide.
let networkingProvider = NetworkingDependencyProvider()
let securityProvider = SecurityDependencyProvider()
container.register(providers: [ networkingProvider, securityProvider ])
-
The default scope used by the container.
Declaration
Swift
public static let defaultScope: DependencyContainer.Scope
-
Creates a new container instance by passing a configuration closure.
Declaration
Swift
public convenience init(configure: (DependencyContainer) -> ())
Parameters
configure
A closure that allows to register dependencies directly at initialization.
-
Creates a new container instance by passing a list of providers to register.
Declaration
Swift
public convenience init(providers: [DependencyProvider])
Parameters
providers
A list of dependency providers to register.
-
Registers a given instance conforming to a single interface. That same instance will always be returned on subsequent calls into the container.
Declaration
Swift
public func register<T>(_ instance: T, as type: T.Type)
Parameters
instance
An initialized instance to register.
type
A type (like a protocol) that the instance supports.
-
Registers a creating closure (factory). A new instance of the registered interface will be created and returned on subsequent calls into the container.
Declaration
Swift
public func register<T>(_ scope: Scope = DependencyContainer.defaultScope, _ factory: @escaping () throws -> T)
Parameters
scope
The strategy to use to manage resolved instances life cycle.
factory
A closure that returns an initialized instance to be registered.
-
Registers a creating closure (factory) conforming to an interface.
Declaration
Swift
public func register<T>(as type: T.Type, _ scope: Scope = DependencyContainer.defaultScope, factory: @escaping () throws -> T)
Parameters
type
A type (like a protocol) that the instance created by the factory supports.
scope
The strategy to use to manage resolved instances life cycle.
factory
A closure that returns an initialized instance to be registered.
-
Registers dependency providers that will automatically register all of the provider’s dependencies.
A
DependencyProvider
allows to group dependency registrations.Declaration
Swift
public func register(providers: [DependencyProvider])
Parameters
providers
A list of dependency providers to register.
-
Declaration
Swift
public func make<T>(_ type: T.Type = T.self) throws -> T
-
Declaration
Swift
public func make<T>(_ type: T.Type = T.self, context: Scope.Context? = nil) throws -> T
-
Declaration
Swift
public var description: String { get }
-
A scope defines the strategy used by the
See moreDependencyContainer
to manage resolved instances life cycle.Declaration
Swift
public enum Scope