0

I am attempting to upgrade from Dagger 2.7 to Dagger 2.21 in an Android app. So far, that has mostly involved adding new scopes to subcomponents, as this is enforced in Dagger 2.8+, but wasn't in Dagger 2.7. I have been stuck for a few hours on an error that I was hoping someone could help me get past. The error is this:

error: [Dagger/MissingBinding] com.experticity.android.member.model.card.survey.SurveyCampaign cannot be provided without an @Inject constructor or an @Provides-annotated method.

I have an @Provides method in a module though:

@Module
class SurveyPlayerModule(private val surveyCampaign: SurveyCampaign) {

    @Provides
    @FragmentScope
    fun provideSurveyCampaign(): SurveyCampaign {
        return surveyCampaign
    }
}

And I provide that surveyCampaign from my fragment:

getComponent().surveyPlayerFragmentComponent(
                    new SurveyPlayerModule(mSurveyCampaign), new FragmentModule(this));

The class that doesn’t seem to be able to get the survey campaign is the SurveyTracker and the constructor looks like this:

@Inject
public SurveyTracker(UserRepository userRepository, CampaignRepository campaignRepository, SurveyCampaign surveyCampaign) {// Set all of the fields from the constructor parameters}

And yes, our project is a mix of Kotlin and Java, moving gradually towards more and more Kotlin.

And the rest of the error message, with package names removed for brevity:

SurveyCampaign is injected at SurveyTracker(…, surveyCampaign)

SurveyTracker is provided at SurveyPlayerFragmentComponent.surveyTracker() [ApplicationComponent → SessionComponent → RepositoryComponent → activity.ActivityComponent → activity.SurveyPlayerFragmentComponent]

  The following other entry points also depend on it:
      SurveyPlayerFragmentComponent.inject(SurveyPlayerViewModel) [ApplicationComponent → SessionComponent → RepositoryComponent → activity.ActivityComponent → activity.SurveyPlayerFragmentComponent]

      SurveyPlayerFragmentComponent.inject(SurveyQuestionViewModel) [ApplicationComponent → SessionComponent → RepositoryComponent → activity.ActivityComponent → activity.SurveyPlayerFragmentComponent]

      SurveyPlayerFragmentComponent.inject(SurveyCompletedViewModel) [ApplicationComponent → SessionComponent → RepositoryComponent → activity.ActivityComponent → activity.SurveyPlayerFragmentComponent]

The full, un-edited error stack:

ApplicationComponent.java:11: error: [Dagger/MissingBinding] com.experticity.android.member.model.card.survey.SurveyCampaign cannot be provided without an @Inject constructor or an @Provides-annotated method.
public abstract interface ApplicationComponent {
                ^
      com.experticity.android.member.model.card.survey.SurveyCampaign is injected at
          com.experticity.android.member.domain.SurveyTracker(…, surveyCampaign)
      com.experticity.android.member.domain.SurveyTracker is injected at
          com.experticity.android.member.ui.viewmodel.SurveyPlayerViewModel.mSurveyTracker
      com.experticity.android.member.ui.viewmodel.SurveyPlayerViewModel is injected at
          com.experticity.android.member.injection.component.activity.SurveyPlayerFragmentComponent.inject(com.experticity.android.member.ui.viewmodel.SurveyPlayerViewModel) [com.experticity.android.member.injection.component.ApplicationComponent → com.experticity.android.member.injection.component.SessionComponent → com.experticity.android.member.injection.component.RepositoryComponent → com.experticity.android.member.injection.component.activity.ActivityComponent → com.experticity.android.member.injection.component.activity.SurveyPlayerFragmentComponent]
  The following other entry points also depend on it:
      com.experticity.android.member.injection.component.activity.SurveyPlayerFragmentComponent.inject(com.experticity.android.member.ui.viewmodel.SurveyQuestionViewModel) [com.experticity.android.member.injection.component.ApplicationComponent → com.experticity.android.member.injection.component.SessionComponent → com.experticity.android.member.injection.component.RepositoryComponent → com.experticity.android.member.injection.component.activity.ActivityComponent → com.experticity.android.member.injection.component.activity.SurveyPlayerFragmentComponent]
      com.experticity.android.member.injection.component.activity.SurveyPlayerFragmentComponent.inject(com.experticity.android.member.ui.viewmodel.SurveyCompletedViewModel) [com.experticity.android.member.injection.component.ApplicationComponent → com.experticity.android.member.injection.component.SessionComponent → com.experticity.android.member.injection.component.RepositoryComponent → com.experticity.android.member.injection.component.activity.ActivityComponent → com.experticity.android.member.injection.component.activity.SurveyPlayerFragmentComponent]

And here is the component:

@FragmentScope
@Subcomponent(modules = [SurveyPlayerModule::class, FragmentModule::class])
interface SurveyPlayerFragmentComponent {

    @ChildFragmentManager
    fun childFragmentManager(): FragmentManager

    fun surveyTracker(): SurveyTracker

    //Fragments

    fun inject(fragment: SurveyPlayerFragment)

    // ViewModels

    fun inject(viewModel: SurveyPlayerViewModel)

    fun inject(viewModel: SurveyQuestionViewModel)

    fun inject(viewModel: SurveyCompletedViewModel)

    // Adapters

    fun inject(adapter: SurveyPlayerAdapter)
}
Nick
  • 11
  • 1
  • 4
  • Please have a [look here](https://stackoverflow.com/q/44912080/1837367) for some general information. If you still need help, please update your question and include the full error "stack" as shown in the linked question. I would guess that you try to inject `SurveyTracker` in a different component where you didn't bind `SurveyCampaign`, thus the errror – David Medenjak Mar 06 '19 at 19:30
  • Thanks for the response, David. I just read over that carefully and didn't find anything that would help me to resolve my particular situation. The complete error stack is in my original post, but it is kind of broken into two pieces, so I will update it to all be in one place with complete packages too. I also posted the component. `SurveyPlayerModule` provides the `SurveyCampaign` and `SurveyPlayerFragmentComponent` is using that module. – Nick Mar 06 '19 at 19:43
  • I should also note that this was working with Dagger 2.7. It stopped working when I updated to Dagger 2.21. I had to change the scope from `@ActivityScope` (which was being using by the parent component) to `@FragmentScope`. Although, I don't know if the scope is causing the problem or not. – Nick Mar 06 '19 at 19:48
  • Does it work if you remove `@FragmentScope` from the `provideSurveyCampaign()`? I can't spot any obvious errors – David Medenjak Mar 06 '19 at 20:03
  • I tried removing `@FragmentScope` first from `provideSurveyCampaign()` (I guess it doesn't really need that, since it is a specific instance anyway). That didn't work, so I tried removing `@FragmentScope` from the Subcomponent as well, and that also didn't work... – Nick Mar 06 '19 at 20:39

1 Answers1

1

I finally figured it out. I kept on thinking, "It feels like SurveyTracker and SurveyPlayerFragment (which actually creates the module and passes the surveyCampaign to the module) are not in the same scope." I was thinking about this from a non-Dagger perspective, but it turns out that the SurveyTracker class was annotated with the @ActivityScope scope instead of @FragmentScope like everything else in the component and module. Changing it to @FragmentScope fixed it. For some reason I had not thought of checking for annotations on the SurveyTracker, because I didn't think that was something you would do. Thank you for your help, David.

Nick
  • 11
  • 1
  • 4