-
Notifications
You must be signed in to change notification settings - Fork 72
Named definitions
Sometimes you need to register different factories or different implementations of the same protocol. DependencyContainer
does not capture the concrete type that you instantiate or any implementation details of registered factory, so if you try this the latest definition will override a previous one:
container.register() { URLSessionNetworkLayer(baseURL: "http://prod.myapi.com/api/") as NetworkLayer }
container.register() { URLSessionNetworkLayer(baseURL: "http://dev.myapi.com/api/") as NetworkLayer }
let network = try! container.resolve() as NetworkLayer
network.baseURL // http://dev.myapi.com/api/
To solve that you can use named definitions by providing tag
parameter to register
method.
- If you provide a
tag
value in the parameter toregister
, it will associate created definition with this tag; - When you provide a
tag
value toresolve
, container will try to find a matching definition associated with this tag. If it doesn't find any, it will fallback to a matching definition not associated with any tag (see note) - Tags can be of any type that conforms to
DependencyTagConvertible
protocol.String
andInt
conform to this protocol,RawRepresentable
type withString
orInt
raw value types provides default implementation ofDependencyTagConvertible
.
Think about untagged definitions as a default factory where you can register more specialized named factories.
enum Environment: String, DependencyTagConvertible {
case Production
case Development
}
container.register(tag: Environment.Production) { URLSessionNetworkLayer(baseURL: "http://prod.myapi.com/api/") as NetworkLayer }
container.register(tag: Environment.Development) { URLSessionNetworkLayer(baseURL: "http://dev.myapi.com/api/") as NetworkLayer }
let networkLayer = try! container.resolve(tag: Environment.Production) as NetworkLayer
network.baseURL // http://prod.myapi.com/api/
If all definitions of the component are registered with a tag then you will not be able to resolve this component without providing a tag.
Even when falling back to untagged definition resolved instance will be associated with the tag that was passed to resolve
. This way even though the same definition was used container will provide separate instances. They can be later reused only if you resolve using the same tag. That affects all scopes including singleton scopes.
container.register(.singleton) { ServiceImp() as Service }
//resolved instance will be associated with empty tag
let service = container.resolve() as Service
//resolved instance will be associated with tag "one"
let service1 = container.resolve(tag: "one") as Service
//will not reuse service as it was resolve for different tag
service !== service1
//resolved instance will be associated with tag "another"
let service2 = container.resolve(tag: "another") as Service
//will not reuse service1, nor service as they were resolved for different tag
service2 !== service1
service2 !== service
let service1_2 = container.resolve(tag: "one") as Service
//will reuse service1 as it was resolved for the same tag
service1 === service1_2
See also: Resolving multiple instances