google/dagger

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

alveru opened this issue · 6 comments

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]

}

  • ViewModelModule.java:
@Module
public abstract class ViewModelModule {

   @Binds
   @IntoMap
   @ViewModelKey(DetailsViewModel.class)
   abstract ViewModel bindDetailsViewModel(DetailsViewModel viewModel);
}

  • AppComponent.java:
@Singleton
@Component(modules = {AppModule.class, .class, ViewModelModule.class})
public interface AppComponent {

    void inject(DetailsActivity activity);

}

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.

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

  • I also tried adding @singleton to the factory but that failed too

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

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

Thanks in advance

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

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

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.

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!