jonsamwell / flutter_simple_dependency_injection

A super simple dependency injection implementation for flutter that behaviours like any normal IOC container and does not rely on mirrors
MIT License
94 stars 8 forks source link

Stack Overflow error #19

Closed comatory closed 3 years ago

comatory commented 3 years ago

Hi. Great package, however I'm running into some nasty error which might or might not be caused by your plugin.

I am using couple of map methods and some of them are not singletons. It looks like this

      _injector.map<ApiClient>(
        (i) => ApiClient(),
        isSingleton: false,
      )
      ..map<AuthorizedApiClient>(
        (i) => AuthorizedApiClient(
          apiClient: i.get<ApiClient>(),
          authenticationRepository: i.get<AuthenticationRepository>(),
          logger: i.get<LoggerService>(),
        ),
        isSingleton: false,
      )
      ..map<AuthenticationApi>(
          (i) => AuthenticationApi(
                  apiClient: i.get<ApiClient>()),
          isSingleton: true)
      ..map<ApiA>(
          (i) => ApiA(
                  apiClient: i.get<AuthorizedApiClient>(
              )),
          isSingleton: true)
      ..map<ApiB>(
        (i) => ApiB(
            apiClient: i.get<AuthorizedApiClient>()),
        isSingleton: true,
      );

Then when I use it later in the code:

      ..map<UserRepository>(
          (i) => UserRepository(
                apiB: i.get<ApiB>(), // <-- problematic line
                errorMonitoringService: i.get<ErrorMonitoringService>(),
                userStorage: i.get<UserStorage>(),
              ),
          isSingleton: true)

When I add the line apiB: i.get<ApiB>() it throws this error

flutter: Stack Overflow
flutter: #0      Injector._makeKeyPrefix (package:flutter_simple_dependency_injection/src/injector.dart:79:3)
#1      Injector._makeKey (package:flutter_simple_dependency_injection/src/injector.dart:77:10)
#2      Injector.get (package:flutter_simple_dependency_injection/src/injector.dart:234:23)
#3      AppContainer._createApiServices.<anonymous closure> (package:subscription_commute_driver_app_flutter/app_container.dart:94:29)
#4      Injector.map.<anonymous closure> (package:flutter_simple_dependency_injection/src/injector.dart:128:26)
#5      TypeFactory.get (package:flutter_simple_dependency_injection/src/type_factory.dart:20:32)
#6      Injector.get (package:flutter_simple_dependency_injection/src/injector.dart:240:26)
#7      AppContainer._createApiServices.<anonymous closure> (package:subscription_commute_driver_app_flutter/app_container.dart:102:24)
#8      Injector.map.<anonymous closure> (package:flutter_simple_dependency_injection/src/injector.dart:128:26)
#9      TypeFactory.get (p<…>

It only happens when I pass apiB dependency to UserRepository constructor. I found out if I remove

..map<ApiB>(
        (i) => ApiB(
           apiClient: i.get<AuthorizedApiClient>() /* <-- REMOVE DEPENDENCY */),
        isSingleton: true,
      )

and change it to

  ..map<ApiB>(
        (i) => ApiB(),
        isSingleton: true,
      )

The error no longer happens. This is not a "fix" as I obviously need the dependency there but it's a point where it fails.

I am aware that this error could be thrown for any number of reasons but would you have any idea where to start looking at the problem? It seems to instantiate ApiB instance just fine. I have checked if AuthorizedApiClient is really being created twice just to make sure (because my code expects each apiClient to be separate instance) by printing out hashCode and they are indeed different.

comatory commented 3 years ago

Also the _injector is created once. I am using class to map dependencies on the injector and then I'm accessing _injector in different methods. When I try to print out _injector.getAll() I get empty () so not sure what that is about.

comatory commented 3 years ago

So after doing some deep diving into my own code, I realized that this is a problem with circular dependency. This was not obvious from the start because in the constructor of one of the dependencies I was creating an instance of service. This service was not managed by DI mechanism and I was passing dependencies directly in there. Unfortunately for me, this service shared the same class that I was trying to use at the same time.

So solution was straightforward from there. The new dependency I created could not use apiB dependency, instead I created another service that serves as a mediator.

It's probably hard to catch Stack Overflow error but if it would be possible, it'd be great to throw custom library error that would give you a hint that you might be attempting to do this. But I assume that it's not something that could be detected in runtime.

You can close this issue, no bug whatsoever.

jonsamwell commented 3 years ago

Sorry missed this. Yes good idea - I'll try and add a circular dependencies error