Access Dagger subcomponent dependencies from parent android module

When Dagger tries to create an object of AuthenticationManager, it will also try to inject any @Inject annotated fields as well. And that doesn't work as you correctly pointed out that PreLoader is from a different scope which essentially means that it is from a different component, i.e., UserComponent.

Important thing to note here is that UserComponent is not yet created at the point AuthenticationManager object is first created by Dagger. And since PreLoader is provided byUserComponent, there is no way Dagger could inject that field. However you can inject that field into AuthenticationManager by yourself.

Something like this:

class AuthenticationManager @Inject constructor(loginApi: LoginApi) {

    // Remove the @Inject annotation and provide a custom setter which will pre-load user data 
    private lateinit var preLoader : PreLoader
    set(preLoader) {
        field = preLoader
        preLoader.preload()
    }

    val authenticationStateStream = Observable<AuthenticationState>()

    fun login() {
        if (success) {
            authenticationStateStream.emit(AuthenticationState.AUTHENTICATED)
            // UserSubComponent is now created
            // You can't do this here:
            // preLoader.preload()
        }
    }
}

Your App class:

class App : DaggerApplication() {

    @Inject
    lateinit var authenticationManager: AuthenticationManager

    override fun onCreate() {
        super.onCreate()

        authenticationManager
            .authenticationStateStream
            .subscribe { state ->
                if (state == AuthenticationState.AUTHENTICATED) {
                    // Here you can directly set the PreLoader object yourself
                    authenticationManager.preLoader = 
                                AppInjector.userComponent.preLoader()
                }
             }
    }
}

In order to access PreLoader object, you also need to modify your UserComponent like this:

@UserScope
@Subcomponent(modules = [UserModule::class, AndroidSupportInjectionModule::class])
interface UserComponent : AndroidInjector<App> {

    fun preLoader() : PreLoader // <-- Need to add this

    @Subcomponent.Builder
    interface Builder {
        fun build(): UserComponent
    }

}

So after a lot of trying, I finally managed to solve my problem.

What I discovered was:

  • Don't use SubComponents when your app is divided into multiple modules.
  • Each module needs it's own component (feature component)

I found this article really helpful: https://proandroiddev.com/using-dagger-in-a-multi-module-project-1e6af8f06ffc