Android Hilt - How to inject viewModelScope - android

I want to inject viewModelscope but I just couldn't do it.
class PostPageSource #Inject constructor(
val repository: MyRepository,
val scope: CoroutineScope,
val mapper: MyMapper
) : PageKeyedDataSource<Int, Posts>() {
The error output is as follows:
[Dagger/MissingBinding] kotlinx.coroutines.CoroutineScope cannot be provided without an #Provides-annotated method.
public abstract static class ApplicationC implements App_GeneratedInjector,
I tried to:
#Module
#InstallIn(FragmentComponent::class)
object PagingModule {
#Singleton
#Provides
fun provideViewModel(fragment: Fragment) : UserDetailViewModel {
val viewModel: UserDetailViewModel by (fragment as UserDetailFragment).viewModels()
return viewModel
}
fun provideCorountineScope(fragment: UserDetailViewModel): CoroutineScope {
return fragment.viewModelScope
}
}
The error output is as follows:
error: [Dagger/MissingBinding] kotlinx.coroutines.CoroutineScope cannot be provided without an #Provides-annotated method.
public abstract static class ApplicationC implements App_GeneratedInjector,
^
kotlinx.coroutines.CoroutineScope is injected at
com.maksu.insider.userdetail.paging.PostPageSource(…, scope, …)
javax.inject.Provider<com.maksu.insider.userdetail.paging.PostPageSource> is injected at
com.maksu.insider.userdetail.paging.PostPageSourceFactory(providerDataSource)
javax.inject.Provider<com.maksu.insider.userdetail.paging.PostPageSourceFactory> is injected at
com.maksu.insider.userdetail.UserDetailViewModel_AssistedFactory(…, dataSourceFactory)
com.maksu.insider.userdetail.UserDetailViewModel_AssistedFactory is injected at
com.maksu.insider.userdetail.UserDetailViewModel_HiltModule.bind(factory)
java.util.Map<java.lang.String,javax.inject.Provider<androidx.hilt.lifecycle.ViewModelAssistedFactory<? extends androidx.lifecycle.ViewModel>>> is injected at
androidx.hilt.lifecycle.ViewModelFactoryModules.ActivityModule.provideFactory(…, viewModelFactories)
#dagger.hilt.android.internal.lifecycle.DefaultActivityViewModelFactory java.util.Set<androidx.lifecycle.ViewModelProvider.Factory> is requested at
dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories.ActivityEntryPoint.getActivityViewModelFactory() [com.maksu.insider.App_HiltComponents.ApplicationC → com.maksu.insider.App_HiltComponents.ActivityRetainedC → com.maksu.insider.App_HiltComponents.ActivityC]
The following other entry points also depend on it:

I don't know why you want to do that, but I think you miss some #Provides at the top of your function:
#Provides
fun provideCorountineScope(fragment: UserDetailViewModel): CoroutineScope {
return fragment.viewModelScope
}
But I really don't recommend doing this. If you want a coroutine that should last as long as the viewmodel does, then just launch the coroutine inside the viewmodel.

Related

Inject an instance of repository class into Application class which is provided by Module with ViewModelScope

I am having a problem that I need inject an instance of repository class into Application class which is provided by Module (Installed in ViewModelComponent, and provide function marked with #ViewModelScope annotation)
Repository
interface IARepository
class ARepository #Inject constructor() : IARepository
Module
#Module
#InstallIn(ViewModelComponent::class)
interface RepositoryModule {
#Binds
#ViewModelScoped
fun provideARepos(impl: ARepository): IARepository
}
ViewModel
#HiltViewModel
class TestViewModel #Inject constructor(
private val useCase1: UseCase1,
private val useCase2: UseCase2,
) {
...
}
Two UseCase1 and UseCase2 are using IARepository, since if I provides IARepository with ViewModelScope, two instance useCase1 and useCase2 will be using the same instance of repository.
It worked until I inject repository into Application (singleton things)
Application
#HiltAndroidApp
class TestApplication : Application() {
#Inject
lateinit var a: IARepository
}
After that I got error
[Dagger/MissingBinding] IARepository cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint
Application_HiltComponents.java:129: error: [Dagger/MissingBinding] ...core.domain.IARepository cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint,
^
A binding for ....core.domain.IARepository exists in ...Application_HiltComponents.ViewModelC:
....core.domain.IARepository is injected at
[...Application_HiltComponents.SingletonC] ...Application.a
...Application is injected at
...Application_HiltComponents.SingletonC] ...Application_GeneratedInjector.injectMoonRoverApplication
In application, I tried switch to inject directly implementation class is ARepository, it worked fine.
#HiltAndroidApp
class TestApplication : Application() {
#Inject
lateinit var a: ARepository
}
But I still want to use interface. Are there any solution for it?
I think you have to use #Provides in module as below
#Module
#InstallIn(ViewModelComponent::class)
interface RepositoryModule {
#Binds
#ViewModelScoped
#Provides
fun provideARepos(impl: ARepository): IARepository
}
Also add #HiltViewModel in your view model as below
#HiltViewModel
class TestViewModel #Inject constructor(
private val useCase1: UseCase1, private val useCase2: UseCase2
) {
...
}
I hope it will help you.
in viewmodel please specify the #HiltViewModel
#HiltViewModel
class TestViewModel #Inject constructor(
private val useCase1: UseCase1, private val useCase2: UseCase2
) {
...
}
edited:-
#Module
#InstallIn(SingletonComponent::class)
object Module {
#Provides
fun ProvideImplRepo() = ImplRepo()
}
#Module
#InstallIn(ViewModelComponent::class)
abstract class RepositoryModule {
#Binds
abstract fun bindLoginRepository(impl: ImplRepo): Repo
}

How to inject a generic type

I have a repository interface that receives a generic type.
interface AuthRepo<R> {
val isUserAuthenticatedInFirebase: Boolean
suspend fun oneTapSignInWithGoogle(): R
suspend fun firebaseSignInWithGoogle(): R
}
The generic type is the class is BeginSignInResult that returns the result of the login. This class is not owned by me, so I have to inject it with a module. Here it is.
#Singleton
class AuthRepoImpl #Inject constructor(
...
#Named(SING_IN_RESULT)
private val singInResult: BeginSignInResult,
#Named(SING_UP_RESULT)
private val singUpResult: BeginSignInResult,
...
) : AuthRepo<BeginSignInResult>
This is the module where I am injecting the repository implementation.
#Module
#InstallIn(ViewModelComponent::class)
abstract class AuthModule {
#Binds
abstract fun provideAuthRepository(
authRepoImpl: AuthRepoImpl
): AuthRepo<BeginSignInResult>
}
The problem
As this class is not my property, I have to inject it with a module. This BeginSignInResult class also needs other classes that are also injected with modules. This is the code that injects it.
#Provides
#Named(AuthConstant.SING_IN_RESULT)
suspend fun provideSingInResult(
oneTapClient: SignInClient,
#Named(AuthConstant.SIGN_IN_REQUEST)
signUpRequest: BeginSignInRequest
) : BeginSignInResult {
return oneTapClient.beginSignIn(signUpRequest).await()
}
#Provides
#Named(AuthConstant.SING_UP_RESULT)
suspend fun provideSingUpResult(...) : BeginSignInResult ...
Hilt Error
This is the compile error. I think the use case that accesses the repository does not need to be shown
com...AuthRepo<com...BeginSignInResult> cannot be provided without an #Provides-annotated method.
com...AuthRepo<com..BeginSignInResult> is injected at
com...OneTapSingInUseCase(authRepo)
com...OneTapSingInUseCase<com...BeginSignInResult> is injected at
com...AuthRepo<com....BeginSignInResult> is injected at
com...OneTapSingInUseCase(authRepo)
com...OneTapSingInUseCase<com...BeginSignInResult> is injected at
com...LoginViewModel(�, oneTapUseCase)
com...LoginViewModel is injected at
com...LoginViewModel_HiltModules.BindsModule.binds(arg0)
#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....App_HiltComponents.SingletonC ? com...App_HiltComponents.ActivityRetainedC ? com...App_HiltComponents.ViewModelC]

Dagger Hilt: Repository cannot be provided without an #Provides-annotated method

Hilt points out that this interface cannot be provided without an #Provides annotation:
interface PlannedListRepository {
fun getAllLists(): LiveData<List<PlannedList>>
suspend fun addList(plannedList: PlannedList)
suspend fun updateList(plannedList: PlannedList)
suspend fun deleteList(plannedList: PlannedList)
}
Implementation of the interface:
class PlannedListRepositoryImpl #Inject constructor(private val plannedListDao: PlannedListDao) :
PlannedListRepository {
...
}
As far as I know, if I want to get an interface, I can use #Binds to say to Dagger which implementation should be received. So I did this:
#Module
#InstallIn(ActivityComponent::class)
abstract class RepositoryModule {
#Binds
abstract fun providePlannedListRepository(impl: PlannedListRepositoryImpl) : PlannedListRepository
}
My ViewModel if it has something to deal with the error:
#HiltViewModel
class PlannedListViewModel #Inject constructor(
private val repository: PlannedListRepository
) : ViewModel() {
...
}
So what should I do to fix the error? The full error message:
AndroidStudioProjects\PlanShopping\app\build\generated\hilt\component_sources\debug\com\tetsoft\planshopping\PlannerApplication_HiltComponents.java:129: error: [Dagger/MissingBinding] com.tetsoft.planshopping.db.planned.PlannedListRepository cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements PlannerApplication_GeneratedInjector,
^
com.tetsoft.planshopping.db.planned.PlannedListRepository is injected at
com.tetsoft.planshopping.ui.planned.PlannedListViewModel(repository)
com.tetsoft.planshopping.ui.planned.PlannedListViewModel is injected at
com.tetsoft.planshopping.ui.planned.PlannedListViewModel_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.tetsoft.planshopping.PlannerApplication_HiltComponents.SingletonC ? com.tetsoft.planshopping.PlannerApplication_HiltComponents.ActivityRetainedC ? com.tetsoft.planshopping.PlannerApplication_HiltComponents.ViewModelC]
Here is the database module:
#Module
#InstallIn(SingletonComponent::class)
class DatabaseModule {
#Provides
#Singleton
fun provideDatabase(#ApplicationContext appContext: Context) : PlannerDatabase {
return Room.databaseBuilder(
appContext,
PlannerDatabase::class.java,
"PlannerDB"
)
.fallbackToDestructiveMigration()
.build()
}
#Provides
fun providePlannedListDao(plannerDatabase: PlannerDatabase) : PlannedListDao {
return plannerDatabase.plannedListDao()
}
#Provides
fun provideProductDao(plannerDatabase: PlannerDatabase) : ProductDao {
return plannerDatabase.productDao()
}
#Provides
fun provideSelectedProductDao(plannerDatabase: PlannerDatabase) : SelectedProductDao {
return plannerDatabase.selectedProductDao()
}
}
use #InstallIn(ViewModelComponent::class) in your repository module since you are injection repository in your view model
Is your application multi modules? If your answer is yes check your imports from your :app, in my case I facing the same error and I forgot to add my other modules where he DI was defined!
Better late than never.
Besides you need do the thing mentioned in the accepted answer.If you are working with compose or the latest Hilt version, init your ViewModel by the way of:
val viewModel: YourViewModel by viewModels()
do not use the way of:
#Inject lateinit var viewModel: YourViewModel

Dagger/Hilt missing binding. Repository cannot be provided without an #Provides-annotated method

I have this interface
interface ShowsScheduleRepository {
suspend fun getShowsSchedule(medium: String, timeSlot: String): ShowsSchedule
}
which is provided in this class
class GetShowsScheduleUseCase #Inject constructor(
private val repository: ShowsScheduleRepository
)
by
#Provides
#Singleton
fun provideShowsScheduleRepository(api: TDRetrofitApi): ShowsScheduleRepository {
return ShowsScheduleRepoImpl(api)
}
and then this class is injected in a viewModel
#HiltViewModel
class HomeFragmentViewModel #Inject constructor(
private val getShowsSchedule: GetShowsScheduleUseCase
) : ViewModel() {
And when I am trying to build I get the error that it cannot provide a ShowsScheduleRepository without an #Provides-annotated method
Note to self: Put the AppModule file in the same Android Module where the HiltEntryPoint is
The provideShowsScheduleRepository method should be placed in a module class (by saying that I mean a class annotated with #Module). Are you sure that this module is installed in the correct scope? If not, try adding #InstallIn(SingletonComponent::class). (You can replace SingletonComponent with any other component that is described here based on your needs)

I can't inject Dao to a Repository Class, with HILT, in Android

i follow this tutorial Room Dependency Injection - MVVM To-Do List App with Flow and Architecture Components #4
Now I want to convert, a hole app that I have with Room, to this Clean Architecture.
In the tutorial Florian uses DI, to inject TaskDao into TaskViewModel, but I have a Repositories clases.
So I get to a point where the app is build without errors.
and this is my Repository:
class AnioRepository constructor(
private val agrotrackerApi: AgrotrackerApi
) {
val TAG = "AnioRepository"
//val anioDao: AnioDao
fun downloadAnios(): AniosResponse? {
GlobalScope.launch(Dispatchers.IO) {
val result = agrotrackerApi.getAnios()
if (result.isSuccessful) {
for (anio in result.body()!!){
Log.d(TAG, anio.toString())
}
}
}
return null
}
fun getAnios() {
//anioDao.getListAnios()
}
}
and this is my RepositoryModule:
#Module
#InstallIn(ApplicationComponent::class)
object RepositoryModule {
#Singleton
#Provides
fun providesAnioRepository( agrotrackerApi: AgrotrackerApi) : AnioRepository {
return AnioRepository(agrotrackerApi)
}
}
So I'm trying to add Dao class to the Repository Class, like this:
class AnioRepository constructor(
private val anioDao: AnioDao,
private val agrotrackerApi: AgrotrackerApi
) {
val TAG = "AnioRepository"
...
and then, change RepositoryModule, to match constructor...
...
fun providesAnioRepository( anioDao: AnioDao, agrotrackerApi: AgrotrackerApi) : AnioRepository
= AnioRepository(anioDao, agrotrackerApi)
...
but when I press Ctrl-F9, i get this error:
public abstract class ATMDatabase extends androidx.room.RoomDatabase {
^C:\pryectos\AndroidStudioProjects\ATMobileXKt\app\build\generated\source\kapt\debug\com\cse\atm\ATMApplication_HiltComponents.java:155:
error: [Dagger/MissingBinding] #com.cse.atm.di.ApplicationScope
kotlinx.coroutines.CoroutineScope cannot be provided without an
#Provides-annotated method. public abstract static class SingletonC
implements ATMApplication_GeneratedInjector,
^
#com.cse.atm.di.ApplicationScope kotlinx.coroutines.CoroutineScope is injected at
com.cse.atm.database.ATMDatabase.Callback(�, applicationScope)
com.cse.atm.database.ATMDatabase.Callback is injected at
com.cse.atm.di.AppModule.providesDatabase(�, callback)
com.cse.atm.database.ATMDatabase is injected at
com.cse.atm.di.AppModule.providesAnioDao(db)
com.cse.atm.database.AnioDao is injected at
com.cse.atm.di.RepositoryModule.providesAnioRepository(anioDao, �)
javax.inject.Provider<com.cse.atm.data.AnioRepository> is injected at
What means this error? What I'm missing ?
#ApplicationScope annotation is in another AppModule.kt, I dont' where is the problem.
Any help wil be appreciated!!
Best Regards
Your ATMDatabase.Callback is requesting a CoroutineScope with custom qualifier #ApplicationScope, but you are not providing such a CoroutineScope in any of your modules.
To provide a coroutine scope, the tutorial you linked adds this code around the 28-minute mark:
#Provides
#Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
Since you are using the #ApplicationScope qualifier when requesting a coroutine scope, you will also need to add the qualifier to this #Provides method:
#Provides
#Singleton
#ApplicationScope
fun provideApplicationScope() = CoroutineScope(SupervisorJob())

Categories

Resources