Dagger 2 #Provides can't be apply to static function in Kotlin? - kotlin

I have the following Java code which works fine
#Module(subcomponents = {MainActivityComponent.class})
abstract public class ActivityBuilder {
#Provides
#Singleton
static Context provideContext(Application application) {
return application;
}
#Binds
#IntoMap
#ClassKey(MainActivity.class)
abstract AndroidInjector.Factory<?> bindMainActivity(MainActivityComponent.Builder builder);
}
When I convert it to Kotlin
#Module(subcomponents = [MainActivityComponent::class])
abstract class ActivityBuilder {
companion object {
#Provides
#Singleton
fun provideContext(application: Application): Context {
return application
}
}
#Binds
#IntoMap
#ClassKey(MainActivity::class)
abstract fun bindMainActivity(builder: MainActivityComponent.Builder): AndroidInjector.Factory<*>
}
I got error stating I can't have my #Provides on the static function.
error: #Provides methods can only be present within a #Module or #ProducerModule
public final android.content.Context provideContext(#org.jetbrains.annotations.NotNull()
^
How could I fix that?

Apparently the below works.
#Module(subcomponents = [MainActivityComponent::class])
abstract class ActivityBuilder {
#Module
companion object {
#JvmStatic
#Provides
#Singleton
fun provideContext(application: Application): Context {
return application
}
}
#Binds
#IntoMap
#ClassKey(MainActivity::class)
abstract fun bindMainActivity(builder: MainActivityComponent.Builder): AndroidInjector.Factory<*>
}

Related

Hilt not detecting dependencies despite #Provides and #Inject

I am practicing clean architecture in an app, so for this I divided the application in modules, App, data, domain. I have created the modules of the dependencies on the data module, where I have created the UseCasesModule. Such Module has been provided with the #Module tag and #Installin(SingletonComponent::class), so on the App module I have added as a module dependency the Domain and Data modules. But when I am trying to inject the UseCase on the ViewModel I get the following error
`
com.example.usecases.GetUserByPersonaNameUseCase cannot be provided without an #Inject constructor or an #Provides-annotated method.
com.example.usecases.GetUserByPersonaNameUseCase is injected at
com.example.dota2stats.di.FactoryModule.providesHomeViewModelFactory(getUserByPersonaNameUseCase)`
This is not my first time with Hilt, so I know that I have to add the #Provides on the method that will provide the dependency, but right now is not working, I have restarted the app plenty of times. How can I solve this? Here my code
Here is the dependency
#Module
#InstallIn(SingletonComponent::class)
class UseCasesModule {
#Singleton
#Provides
fun providesGetUserByPersonaNameUseCase(repository: Repository): GetUserByPersonaNameUseCase {
return GetUserByPersonaNameUseCase(repository)
}
}
Here the ViewModel where I am trying to inject it
class HomeViewModel (private val getUserByPersonaNameUseCase: GetUserByPersonaNameUseCase) : BaseViewModel() {
override fun handleAction(action: Action): Flow<State> {
TODO("Not yet implemented")
}
}
I have tried by adding the #Inject constructor tag like this but still no results
class HomeViewModel #Inject constructor(private val getUserByPersonaNameUseCase: GetUserByPersonaNameUseCase) : BaseViewModel() {
override fun handleAction(action: Action): Flow<State> {
TODO("Not yet implemented")
}
}
Here the ViewModelFactory
class HomeViewModelFactory #Inject constructor(private val getUserByPersonaNameUseCase: GetUserByPersonaNameUseCase) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return HomeViewModel(getUserByPersonaNameUseCase) as T
}
}
And here is how I am trying to use it
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
#Inject
lateinit var homeViewModelFactory: HomeViewModelFactory
private lateinit var homeViewModel: HomeViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
homeViewModel = ViewModelProvider(this, homeViewModelFactory)[HomeViewModel::class.java]
}
}
Did you try to annotate the ViewModel with #HiltViewModel?
#HiltViewModel
class HomeViewModel #Inject constructor(private val getUserByPersonaNameUseCase: GetUserByPersonaNameUseCase) : BaseViewModel() {
override fun handleAction(action: Action): Flow<State> {
TODO("Not yet implemented")
}
}

error: [Dagger/MissingBinding] core.domain.usecase.NotePreferencesUseCase cannot be provided without an #Provides-annotated method

I am trying to create sharedpreferences using Hilt. Already take a look for some search but I get this error.
error: [Dagger/MissingBinding] core.domain.usecase.NotePreferencesUseCase cannot be provided without an #Provides-annotated method.
Here's my code
NotePreferenceRepository
#Singleton
class NotePreferencesRepository #Inject constructor(
private val sharedPreferences: SharedPreferences
): INotePreferencesRepository {
private val edit = sharedPreferences.edit()
override fun setTheme(theme: String) {
edit.putString(THEME, theme)
}
override fun getTheme(): String? {
return sharedPreferences.getString(THEME, "")
}
companion object {
private val THEME = "theme"
}
}
NotePreferencesModule
#Module
#InstallIn(SingletonComponent::class)
object NotePreferencesModule {
#Provides
#Singleton
fun provideNotePreferences(#ApplicationContext context: Context): SharedPreferences {
return context.getSharedPreferences("note_prefs", Context.MODE_PRIVATE)
}
}
Repository Module
#Module
#InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
#Binds
abstract fun providePreferencesRepository(notePreferencesRepository: NotePreferencesRepository): INotePreferencesRepository
}
INotePreferencesRepository
interface INotePreferencesRepository {
fun setTheme(theme: String)
fun getTheme(): String?
}
NotePreferencesInteractor
class NotePreferencesInteractor #Inject constructor(private val notePreferencesRepository: INotePreferencesRepository): NotePreferencesUseCase {
override fun setTheme(theme: String) {
notePreferencesRepository.setTheme(theme)
}
override fun getTheme(): String? = notePreferencesRepository.getTheme()
}
NotePreferencesUseCase
interface NotePreferencesUseCase {
fun setTheme(theme: String)
fun getTheme(): String?
}
SettingViewModel
#HiltViewModel
class SettingViewModel #Inject constructor(private val notePreferencesUseCase: NotePreferencesUseCase): ViewModel() {
fun getTheme() = notePreferencesUseCase.getTheme()
fun setTheme(theme: String) {
notePreferencesUseCase.setTheme(theme)
}
}
SOLVED
forgor to add AppModule
#Module
#InstallIn(ViewModelComponent::class)
abstract class AppModule {
#Binds
#ViewModelScoped
abstract fun provideNotePreferencesUseCase(notePreferencesInteractor: NotePreferencesInteractor): NotePreferencesUseCase
}
So view model can use usecase

What's the meaning of interface as dependency of a class in Kotlin?

interface SomeInterface {
fun someFunction()
}
class SomeClass(private val someInterface: SomeInterface) {
}
What does it mean? As far as I know, interface can't instantiate objects and if it can then where should I implement someFunction()?
You are correct that you cannot instantiate SomeInterface directly, but you can pass implementations of your interface to SomeClass. This way SomeClass can use someFunction() but doesn't care about the lower-level implementation details of the interface (aka polymorphism).
interface SomeInterface {
fun someFunction()
}
class SomeClass(private val someInterface: SomeInterface) {
fun doSomething() = someInterface.someFunction()
}
class SomeImplementation(): SomeInterface {
override fun someFunction() {
println("did something")
}
}
fun main() {
val someClass = SomeClass(SomeImplementation())
someClass.doSomething()
}

Can't provide dependency from subcomponent

I'm new in Dagger2 and learn it in test project. I want to provide SuperObject from subcomponent with custom scope but got error: [Dagger/MissingBinding] ru.rsb.daggerproject.objects.SuperObject cannot be provided without an #Inject constructor or an #Provides-annotated method. I aleready indicated method provides SuperObject with #Provides annotation. Please explain me my mistakes.
I have such graph:
#Singleton
#Component(modules = [AndroidInjectionModule::class, ModuleA::class, ModuleB::class, ActivityModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: App)
}
#Module(subcomponents = [Subcomponent::class])
class ModuleB {
#Singleton
#Provides
fun provideDepB(): DepB {
return DepB(Random.nextInt())
}
#Singleton
#Provides
fun provideObjectWithDepB(value: DepB): ObjectWithDepB {
return ObjectWithDepB.newInstance(value)
}
}
#Subcomponent(modules = [SubcomponentModule::class])
#CustomScope
interface Subcomponent {
#Subcomponent.Builder
interface Builder {
fun subcomponentModule(subModule: SubcomponentModule): Builder
fun build(): ru.rsb.daggerproject.Subcomponent
}
}
#Module
class SubcomponentModule {
#CustomScope
#Provides
fun provideSuperObject(value: ObjectWithDepB): SuperObject = SuperObject.newInstance(value)
}

cannot be provided without an #Inject constructor or an #Provides-annotated method

This my App Component class
#Singleton
#Component(dependencies = {}, modules = {AppModule.class,RoomModule.class, NetworkModule.class})
public interface AppComponent {
void inject(CSATApplication applicationController);
void inject(AverageScoreActivity averageScoreActivity);
CsatDao csaatdao();
CSATDatabase csatDatabase();
RemoteRepositoryImpl getRemoteRepo();
LocalRepositoryImpl getLocalRepo();
Application application();
BaseUrlHolder provideBaseUrlHolder();
Retrofit getRetrofit();
CompositeDisposable getCompositeDisposable();
CompositeDisposable getVMCompositeDisposable();
}
#Module
public class RoomModule {
private CSATDatabase CSATDatabase;
public RoomModule(Application mApplication) {
CSATDatabase = Room.databaseBuilder(mApplication, CSATDatabase.class, AppConstants.DATABASE_NAME).build();
}
#Singleton
#Provides
CSATDatabase providesRoomDatabase() {
return CSATDatabase;
}
#Singleton
#Provides
CsatDao providesCsatDao(CSATDatabase CSATDatabase) {
return CSATDatabase.getCsatDao();
}
#Singleton
#Provides
public RemoteRepository getRemoteRepo(NetworkService networkService){
return new RemoteRepositoryImpl(networkService);
}
#Singleton
#Provides
public LocalRepository getLocalRepo(CsatDao csatDao, Executor exec){
return new LocalRepositoryImpl(csatDao, exec);
}
#Provides
#Named("activity")
public CompositeDisposable getCompositeDisposable(){
return new CompositeDisposable();
}
#Provides
#Named("vm")
public CompositeDisposable getVMCompositeDisposable(){
return new CompositeDisposable();
}
Injecting in activity
#Inject #field:Named("activity")
lateinit var compositeDisposable: CompositeDisposable
#Inject
lateinit var averageViewModelFactory: AverageViewModelFactory
#Inject
lateinit var averageViewModel: AverageViewModel
Injecting In View Model Factory
class AverageViewModelFactory #Inject
constructor() : #JvmSuppressWildcards ViewModelProvider.Factory {
#Inject
lateinit var localRepository: LocalRepository
#Inject
lateinit var remoteRepository: RemoteRepository
#Inject #field:Named("vm")
lateinit var compositeDisposable: CompositeDisposable
#Override
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AverageViewModel::class.java)) {
return AverageViewModel(localRepository, remoteRepository, compositeDisposable) as T
}
throw IllegalArgumentException("Wrong ViewModel class")
}
}
class AverageViewModel #Inject constructor(
val localRepository: LocalRepository,
val remoteRepository: RemoteRepository,
val compositeDisposable: CompositeDisposable
) : BaseViewModel<AverageInteractor.view>(){
Gradel
//Dependencies injection
implementation 'com.google.dagger:dagger:2.19'
implementation 'com.google.dagger:dagger-android:2.19'
kapt 'com.google.dagger:dagger-compiler:2.19'
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
kapt 'com.google.dagger:dagger-android-processor:2.19'
compileOnly 'javax.annotation:jsr250-api:1.0'
Error
error: [Dagger/MissingBinding] io.reactivex.disposables.CompositeDisposable cannot be provided without an #Inject constructor or an #Provides-annotated method.
public interface AppComponent {
^
io.reactivex.disposables.CompositeDisposable is provided at
com.sisindia.csat.deps.AppComponent.getCompositeDisposable()
It is also requested at:
com.sisindia.csat.projectmodules.score.AverageViewModel(…, compositeDisposable)
The following other entry points also depend on it:
com.sisindia.csat.deps.AppComponent.inject(com.sisindia.csat.projectmodules.score.AverageScoreActivity)
com.sisindia.csat.deps.AppComponent.getVMCompositeDisposable()
You need to annotate your CompositeDisposable's with #Named in AppComponent and AverageViewModel classes, like this:
public interface AppComponent {
...
#Named("activity") CompositeDisposable getCompositeDisposable();
#Named("vm") CompositeDisposable getVMCompositeDisposable();
}
class AverageViewModel #Inject constructor(
...
#Named("vm") val compositeDisposable: CompositeDisposable
) : BaseViewModel<AverageInteractor.view>(){