google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.42k stars 2.01k forks source link

@AssistedInject in ViewModel using Dagger (NOT Hilt, just dagger) does not compile: cannot be provided without an @Inject constructor or an @Provides-annotated method #4255

Closed alveru closed 7 months ago

alveru commented 7 months ago

I am using Android Studio - Dagger 2.35.1 (Not Hilt, just dagger), the ViewModel injection works fine using @Inject constructor() pattern, all is fun and games, but i am passing the id through a function to viewmodel which is something i do not like, so, now I am trying to implement an @Assisted param in ViewModel so the ViewModel receives the param during the construction, this param comes in the activity's intent.

After adding @AssistedInject constructor() pattern to ViewModel, now dagger complains:

[Dagger/MissingBinding DetailsViewModel cannot be provided without an @Inject constructor or an @Provides-annotated method

I have searched in all the questions and examples but nobody explains the full solution or why dagger sends that error, they just post sections of code, at this point i am out of ideas what to modify to make it work

class DetailsViewModel @AssistedInject constructor(
    application: Application,
    val repository: DetailsRepository,
    @Assisted val id: String,
) : BaseViewModel(application) {

    @AssistedFactory
    interface Factory { fun create(id: String) : DetailsViewModel }

    companion object {
        @Suppress("UNCHECKED_CAST")
        fun factory(
            factory: Factory,
            id: String
        ) : ViewModelProvider.Factory {
            return object : ViewModelProvider.Factory {
                override fun <T : ViewModel> create(modelClass: Class<T>): T =
                    factory.create(id) as T
            }
        }
    }

class DetailsActivity : AppCompatActivity() {

    @Inject
    lateinit var viewModelFactory: DetailsViewModel.Factory

    private lateinit var viewModel: DetailsViewModel

     override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
        (application as MyApplication).appComponent.inject(this)

        val id = intent.getStringExtra("ID").orEmpty()

        viewModel = ViewModelProvider(this, DetailsViewModel.factory(
            viewModelFactory, id
        ))[DetailsViewModel::class.java]

}
@Module
public abstract class ViewModelModule {

   @Binds
   @IntoMap
   @ViewModelKey(DetailsViewModel.class)
   abstract ViewModel bindDetailsViewModel(DetailsViewModel viewModel);
}
@Singleton
@Component(modules = {AppModule.class, .class, ViewModelModule.class})
public interface AppComponent {

    void inject(DetailsActivity activity);

}
kuanyingchou commented 7 months ago

Hi, @alveru , the missing binding error says that Dagger doesn't know how to provide DetailsViewModel. This is because now that DetailsViewModel is assisted injected, it is not included in the graph anymore. You can try removing your bindDetailsViewModel() or ViewModelModule.

alveru commented 7 months ago

Hello ,

thanks for your prompt response, what you mentioned solved that problem, though now the Factory is the one sending the error, since it cannot be injected :(

error: [Dagger/MissingBinding] DetailsViewModel.Factory cannot be provided without an @Provides-annotated method. void inject(DetailsActivity activity);

i tried putting @AssistedInject too in the variable but didn't work:

class DetailsActivity : AppCompatActivity() { @AssistedInject lateinit var viewModelFactory: DetailsViewModel.Factory

Do you know where should i place the provider so Dagger can inject it?

Thanks in advance

kuanyingchou commented 7 months ago

Hi, @alveru , you shouldn't need a provider for DetailsViewModel.Factory. Could you share a minimal project to reproduce this?

alveru commented 7 months ago

Hi @kuanyingchou

thanks for your reponse, yes, i am attaching a dummy project with the similar configuration to that of my project, with java & kotlin files, it shows the same @Provides error when trying to inject the viewmodel assisted factory, the project is in a zip file:

AssistedTest.zip

kuanyingchou commented 7 months ago

Hi, @alveru , I think it's because you're mixing Dagger 2.35.1 and 2.17 in you build.gradle.kts:

    implementation("com.google.dagger:dagger-android:2.35.1")
    //implementation("com.google.dagger:dagger-android-support:2.17")
    annotationProcessor("com.google.dagger:dagger-android-processor:2.17")
    annotationProcessor("com.google.dagger:dagger-compiler:2.17")
    //debugImplementation("androidx.compose.ui:ui-test-manifest:1.8.2")
    kapt("com.google.dagger:dagger-android-processor:2.17")
    kapt("com.google.dagger:dagger-compiler:2.17")

You'll have to allign all Dagger artifacts to the same version and it has to be higher than 2.31, where assisted injection was introduced.

alveru commented 7 months ago

hi @kuanyingchou ,

thanks for your quick response, i tried it in the dummy project and then in the real project, i moved versions and now is working,

implementation 'com.google.dagger:dagger-android:2.51' //implementation 'com.google.dagger:dagger-android-support:2.17' //annotationProcessor 'com.google.dagger:dagger-android-processor:2.17' annotationProcessor 'com.google.dagger:dagger-compiler:2.48' //kapt 'com.google.dagger:dagger-android-processor:2.17' kapt 'com.google.dagger:dagger-compiler:2.48'

i am closing the ticket, thanks for your prompt support!