AliSoftware / Dip

Simple Swift Dependency container. Use protocols to resolve your dependencies and avoid singletons / sharedInstances!
MIT License
978 stars 75 forks source link

Overriding dependencies with Auto wiring #183

Closed davidkraus closed 6 years ago

davidkraus commented 6 years ago

Hey, we are trying to override some dependencies when a debug flag is enabled:

code looks something like this:

container.register(.singleton, type: SomeService.self) {
  SomeServiceProduction(dependency:$0)
}

if debug == true {
  container.register(.singleton, type: SomeService.self) {
    SomeServiceDebug(dependency:$0, otherDependencyForMocking: $1)
  }
}

This does not work, Dip registers both instances because they have different arguments. But using container.resolve() instead of auto wiring works because then dip does not know about the arguments.

Is this a bug or the desired behavior?

ilyapuchka commented 6 years ago

I'm confused, auto wiring is exactly when you use resolve without parameters. So in this case it should use definition with most number of arguments, in debug it should use definition with two arguments. It will work if all dependencies for parameters are registered too, it will not try another definition if something fails. Here is a reference to implementation for that. Definitions will not though override each other so you will be able to resolve both of them depending on what parameters you send to resolve. Maybe you should always register only one dependency.

davidkraus commented 6 years ago

Sorry i think my explanation was not clear enough.

Just a short description what we thought would happen:

We actually build two apps (debug and production). Some of our ViewModels depend on the SomeService class. So all that is happening is we call container.resolve() as SomeService - we do not care about what parameters the actual implementation needs as long as they are registered in Dip. So Dip will see different calls to the register function depending on the app we're building. Looks something like this:

 DependencyContainer.configure(container: container)
#if DEBUG_MODULES
  // override dependencies for easier testing
  DependencyContainer.configureDebug(container: container)
#endif

Our current "workaround" is to use container.resolve() inside the register function so Dip does not know about the dependencies, like so:

container.register(.singleton,` type: SomeService.self) {
  SomeServiceProduction(dependency: container.resolve() as Dependency)
}

if debug == true {
  container.register(.singleton, type: SomeService.self) {
    SomeServiceDebug(
      dependency:container.resolve() as Dependency, 
      otherDependencyForMocking: container.resolve() as OtherDependencyForMocking
    )
  }
}

this gives us the desired behavior since now SomeService is overridden with the debug implementation.

Thanks for your fast response

ilyapuchka commented 6 years ago

I think this is expected behaviour. In debug you register factory that is different from factory that you registered before for production configuration and so they don't override each other. But when you are registering factories which resolve dependencies manually they both do not accept any parameters, so production factory will be overridden. I would suggest not to register several factories for the same type unless it's really needed.