Other Classes

The following classes are available globally.

Dependency Container

  • 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 ])
    
    See more

    Declaration

    Swift

    public final class DependencyContainer : DependencyResolver
    extension DependencyContainer: CustomStringConvertible