Pemba Tamang
Pemba Tamang

Reputation: 1052

Dagger Hilt Cannot Inject String with another String as a dependency in a ViewModel

These are my hilt dependencies -

//Dagger Hilt
implementation "com.google.dagger:hilt-android:2.33-beta"
kapt "com.google.dagger:hilt-compiler:2.33-beta"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
kapt "androidx.hilt:hilt-compiler:1.0.0-beta01"

These are my two modules

AppModule.kt

    @Module
    @InstallIn(SingletonComponent::class) //scope - entire application
    object AppModule {
    
        @Singleton // single instance in application
        @Provides // provide this dependency
        @Named(Consts.String1) // name in case of conflict
        fun provideTestString1() = "Injected String from String1"
    
    }

MainModule.kt

@Module
@InstallIn(ActivityComponent::class) // scope - activities
object MainModule {

    @ActivityScoped // single instance in Activity
    @Provides // provide this dependency
    @Named(Consts.String2) //name in case of conflict
    fun provideTestString2(@ApplicationContext context:Context,@Named(Consts.String1) testStringFromApplication:String) = context.getString(R.string.string_to_inject) +" $testStringFromApplication"

}

This is my ViewModel

@HiltViewModel
class TestViewModel @Inject  constructor(@Named(Consts.String2) testStringFromViewModel: String) : ViewModel() {

    init {
        Log.d(Consts.TAG, "String from viewModel : $testStringFromViewModel")
    }
}

This is the activity where I am just trying to log the injected strings.

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private  val viewmodel : TestViewModel by viewModels() // injecting viewModel

    @Inject
    @Named(Consts.String1)
    lateinit var testString : String
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(Consts.TAG, testString)

       viewmodel
    }
}

Consts.kt

object Consts {
    const val TAG = "mDependencyInjection"
    const val String1 = "String1"
    const val String2 = "String2"
}

The error

public abstract static class SingletonC implements MyApplication_GeneratedInjector,
                         ^
      @javax.inject.Named("String2") java.lang.String is injected at
          com.example.daggerhilt.TestViewModel(testStringFromViewModel)
      com.example.daggerhilt.TestViewModel is injected at
          com.example.daggerhilt.TestViewModel_HiltModules.BindsModule.binds(vm)
      @dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at
          dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.example.daggerhilt.MyApplication_HiltComponents.SingletonC ? com.example.daggerhilt.MyApplication_HiltComponents.ActivityRetainedC ? com.example.daggerhilt.MyApplication_HiltComponents.ViewModelC]

When I Inject String 1 in the viewmodel it works fine but when I try to inject String 2 the viewmodel. The error above pops up.

Link to the tutorial I am following https://www.youtube.com/watch?v=ZE2Jkvnk2Bs&ab_channel=PhilippLackner

https://github.com/philipplackner/HiltTutorial

Upvotes: 6

Views: 7121

Answers (1)

H&#229;kon Schia
H&#229;kon Schia

Reputation: 1001

A ViewModel outlives an Activity, so you cannot provide a dependency scoped to an Activity to a ViewModel. Your String1 dependency is scoped to Singleton, so it can be used everywhere, but String2 is scoped to an Activity, so it cannot be used in a ViewModel, which is why it doesn't work in your example.

Adjust MainModule to be scoped higher and it will work. You can see the scope hierarchy here. So to inject into a ViewModel, use anything above ActivityComponent/@ActivityScoped, such as

@Module
@InstallIn(ViewModelComponent::class)
object MainModule {

    @Provides
    @ViewModelScoped
    @Named(Consts.String2)
    fun provideTestString2(@ApplicationContext context:Context, @Named(Consts.String1) testStringFromApplication:String) = context.getString(R.string.string_to_inject) +" $testStringFromApplication"
}

Upvotes: 9

Related Questions