Singleton not honored when collaborating containers
jtwigg opened this issue · 3 comments
Help! I've found what I believe to be a nasty bug with Collaborating containers and singleton registrations.
If I have a registration of ServiceA in a rootContainer as a singleton registration and I
a) Resolve the ServiceA from the rootContainer
b) Make a child container the collaborates with the rootContainer
c) resolve ServiceA for a second time with rootContainer
The second resolve of ServiceA is a new instance. I think the call to collaborate with rootContainer wipes the registry clean!
class ServiceA {}
func testSingletonNotHonored() {
var count = 0
let rootContainer = DependencyContainer()
rootContainer.register(.singleton) { () -> ServiceA in
count = count + 1
return ServiceA()
}
let s1: ServiceA = try! rootContainer.resolve()//<<<< Aquire the service
let childContainer = DependencyContainer()
childContainer.collaborate(with: rootContainer) // <<< Collaborate with root service
//NOTE: I believe the root service is now broken. Its forgotten about its
//singleton registration of ServiceA
let s2: ServiceA = try! rootContainer.resolve()
XCTAssert(s1 === s2) //<<<<<< FAils
XCTAssert(count == 1) //<<<<< FAILS
}
NOTE: If you resolve the ServiceA twice After the collaboration, the singleton is honored.
This is important to our team because we want to make modules that are isolated from each other, but have access to root container singletons and this is the only approach that can do this.
I even modified your Collaboration Wiki example to show that the DataStore singleton can be broken.
Digging through the collaboration code, I'm worried that this whole approach is never going to work.
Whenever anyone collaborates with another container, it
a) overrides all its resolved instances in ...
public func collaborate(with containers: [DependencyContainer]) {
(which is almost certainly the bug I'm seeing)
b) It then recurses into the collborators->collaborators and over rights its resolved instances too. It forces them to use the same 'singletonsBox'. This is a sort of coerced collaboration.
An example of this is in your wiki example
https://github.com/AliSoftware/Dip/wiki/containers-collaboration
The following collaboration match up
eventsListModule.collaborate(with: addEventModule, rootContainer)
addEventModule.collaborate(with: eventsListModule, rootContainer)
can be replaced with
eventsListModule.collaborate(with: rootContainer)
addEventModule.collaborate(with: rootContainer)
and now eventsListModule & addEventModule are coerced into collaborating with each other.
I would expect the test to fail, but it passes.
So be it:
I'm opening a question regarding child containers as a result.