AliSoftware/Dip

Question: How to create isolated containers

Closed this issue · 2 comments

How can I create two isolated child containers whereby I have

a) A root container with registered singleton services
b) Two child containers which are independent of each other and may even have similar & conflicting registrations.

The children have access to the Root, but not each other. The Root doesn't have access to the children.

This is in the vain of Daggers Subcomponents and Guices Private Module or the always hideous Swinject Container Hierarchy

An example of this would be where I have two flows, one for logged in and one for unlogged in users.

class ServiceA {}

  class Password {
    let text: String
    let service : ServiceA

    init(text:String, service: ServiceA) {
      self.text = text
      self.service = service
    }
  }


  func testIsolationContainer() {

    let rootContainer = DependencyContainer()

    var count = 0
    rootContainer.register(.singleton) { () -> ServiceA in
      count = count + 1
      return ServiceA()
    }

    let loggedIn = DependencyContainer()
    loggedIn.register(.singleton) { Password(text: "1234", service:$0) }

    let unloggedIn = DependencyContainer()
    unloggedIn.register(.singleton) { Password(text: "-none-", service:$0) }

    // NOTE: Isolated containers
    loggedIn.collaborate(with: rootContainer) //Isolated containers
    unloggedIn.collaborate(with: rootContainer)//Isolated Container

    let passwordA = try! unloggedIn.resolve() as Password

    XCTAssert(passwordA.text == "-none-") //<< FAILS
    XCTAssert(count == 1) ////<< Works

    let passwordB = try! loggedIn.resolve() as Password
    XCTAssert(passwordB.text == "1234")  //<<<Works
    XCTAssert(count == 1)
  }
  • I think the behavior changed with bug #148 : Prior to this, the test failed differently; The containers were isolated but the SerivceA was created twice.

  • Having read the Collaboration code, both unlogged and logged in are bound together to be collaborators since it recurses through the dependencies->dependancies.

  • If I remove the loggedIn.collaborate(...) lines, it fails because it can't find the service

  • The option to go directly to rootContainer.resolve() instead works...
    loggedIn.register(.singleton) { Password(text: "1234", service:rootContainer.resolve()) }
    but is not very desirable because it means when I setup my containers, I have to have extra knowledge about how the dep was registered and with what container, somewhat defeating the purpose of Dip.

Thoughts?

@ilyapuchka So far so good with that change. Thanks for the speedy response.
I'd be open to writing a litany of unit tests on your behalf. I have pretty clear ideas about how I'd like to lay our our dependency graph.

@ilyapuchka Adding comments and tests to your PR.