Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One concrete class for multiple singletons #196

Closed
Narayane opened this issue Jun 27, 2018 · 6 comments
Closed

One concrete class for multiple singletons #196

Narayane opened this issue Jun 27, 2018 · 6 comments

Comments

@Narayane
Copy link

Hi,

I have created 3 protocols for evolutivity and testability reasons. For now, I have only one concrete class which implements these protocols.

I declare this in my DI configuration:

self.register(.singleton) { MyDataSource() as AuthProtocol }
self.register(.singleton) { MyDataSource() as RealTimeDatabaseProtocol }
self.register(.singleton) { MyDataSource() as FileStorageProtocol }

Will MyDataSource be instantiated only one time by Dip?
If not, how can I do this?

Thanks.

@iAmNaz
Copy link

iAmNaz commented Jun 27, 2018

@Narayane can you just register it with a tag?

When it is needed just cast it to how you will use it?

Named Definitions

@Narayane
Copy link
Author

Narayane commented Jun 27, 2018

@iAmNaz Not sure to understand your words, can you give an example relative to my case.

I think my aim is a little bit different, if i understand well named definitions examples. I don't want to have different implementations for a given protocol (dependant of an environment or whatever). I want to be sure thanks to Dip than only one instance of a concrete class will be used to resolve 3 different singletons

@iAmNaz
Copy link

iAmNaz commented Jun 27, 2018

Assuming this is your concrete class, correct?
class MyDataSource: AuthProtocol, RealTimeDatabaseProtocol, FileStorageProtocol {}

Registration:
container.register(tag: "DataSourceTag") { MyDataSource() as MyDataSource }
OR
container.register(.singleton) { MyDataSource() as MyDataSource }

Usage:
let dataSource = try container.resolve(tag: "DataSourceTag" ) as MyDataSource
OR
let dataSource = try container.resolve( ) as MyDataSource

Use your datasource as normal and if you need it to be of some protocol then just cast it
let auth = dataSource as! AuthProtocol

@ilyapuchka
Copy link
Collaborator

@Narayane try to use type forwarding for this - https://github.com/AliSoftware/Dip/wiki/type-forwarding

@ilyapuchka
Copy link
Collaborator

@iAmNaz named definitions should be used to distinguish registrations of the same type which return different implementations depending on label, in this case its required to resolve different types to the same instance, so even if named definitions would work that would be a not a good solution IMO

@Usipov
Copy link

Usipov commented Jun 27, 2018

We have a solution to perform registartion chaining.
Assuming the Class conforms to Protocol1 and Protocol2:

container.register {
    Class()
}.reregister {
    $0 as Protocol1
}.reregister {
    $0 as Protocol2
}

with another scope:

container.register(.singleton) {
    Class()
}.reregister {
    $0 as Protocol1
}.reregister {
    $0 as Protocol2
}

Implementation:

public final class ContainerPromise<T, U> {
    
    let container: DependencyContainer
    let definition: Definition<T, U>
    
    init(
        _ definition: Definition<T, U>,
        _ container: DependencyContainer)
    {
        self.definition = definition
        self.container = container
    }
    
    @discardableResult public func reregister<T2>(
        factory: @escaping (T) -> T2)
        -> ContainerPromise<T, U>
    {
        container.register(
            resolvable: T.self,
            factory: factory
        )
        return self
    }

}

public extension DependencyContainer {
    func promise<T, U>(_ definition: Definition<T, U>) -> ContainerPromise<T, U> {
        return ContainerPromise(definition, self)
    }
}

public extension DependencyContainer {
    @discardableResult public func register<T, R>(
        _ scope: ComponentScope = .shared,
        resolvable: R.Type,
        type: T.Type = T.self,
        tag: DependencyTagConvertible? = nil,
        factory: @escaping (R) -> T)
        -> ContainerPromise<T, ()>
    {
        let definition = register(scope, type: type, tag: tag) { [weak self] () -> T in
            guard let strongSelf = self else {
                fatalError(
                    "Can't register object with type \(T.self) " +
                    "because container deallocated"
                )
            }
            let definition = factory((try strongSelf.resolve() as R))
            return definition
        }
        return promise(definition)
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants