Migrating to Hilt from Dagger on a MVC-project

Viewed 23

I'm trying to migrate an existing project to Hilt (from Dagger), and I've run into an issue that I can't figure out. Since it is an existing project using (a form of) MVC-structure, most guides don't seem to account for my setup, since it doesn't rely on ViewModels.

I've started migrating the activity:

@Module
@InstallIn(ActivityComponent::class)
abstract class ActivityModule {

@Binds
abstract fun bindPresenter(presenter: MainPresenter): MainContract.Presenter

@Binds
abstract fun bindActivity(view: MainActivity): MainContract.View

@Binds
abstract fun provideAccidentPresenter(presenter: AccidentPresenter): AccidentContract.Presenter

@Binds
abstract fun provideAccidentActivity(view: AccidentActivity): AccidentContract.View
}

@InstallIn(ActivityComponent::class)
@Module
object ActivitiesModule {

@Provides
fun provideMainActivity(activity: Activity): MainActivity {
    return activity as MainActivity
}

@Provides
fun provideAccidentActivity(activity: Activity): AccidentActivity {
    return activity as AccidentActivity
}
}

So far, so good. But when it comes to setting up the bind between the View (Fragment) and the presenter, I have this module:

@Module
@InstallIn(SingletonComponent::class)
abstract class DashboardPresenterModule {

    @Binds
    abstract fun bindDashboardPresenter(presenter: DashboardPresenter): DashboardContract.Presenter

    @Binds
    abstract fun bindDashboardView(view: DashboardFragment): DashboardContract.View
}

Both the DashboardPresenter and the DashboardView contains @Inject-annotations that references each-other:

class DashboardPresenter @Inject constructor() : DashboardContract.Presenter, LocationPermissionPresenter() {

    @Inject
    lateinit var view: DashboardContract.View

    @Inject
    lateinit var api: Api

    @Inject
    lateinit var preferences: Preferences

[...]
}

@AndroidEntryPoint
class DashboardFragment : PermissionBaseFragment(), DashboardContract.View, View.OnClickListener {

    @Inject
    lateinit var preferences: Preferences

    @Inject
    lateinit var presenter: DashboardContract.Presenter

[...]
}

The code above doesn't compile, and gives the following error

/Users/zippert/git/[** redacted**]/app/build/generated/hilt/component_sources/debug/[** redacted**]/app/App_HiltComponents.java:141: error: [Dagger/MissingBinding] [** redacted**].ui.dashboard_screen.DashboardFragment cannot be provided without an @Inject constructor or an @Provides-annotated method. This type supports members injection but cannot be implicitly provided.
  public abstract static class SingletonC implements App_GeneratedInjector,
                         ^
  A binding for [** redacted**].ui.dashboard_screen.DashboardFragment exists in [** redacted**].app.App_HiltComponents.FragmentC:
      [** redacted**].ui.dashboard_screen.DashboardFragment is injected at
          [[** redacted**].app.App_HiltComponents.SingletonC] [** redacted**].di.mvp.DashboardPresenterModule.bindDashboardView(view)
      [** redacted**].ui.dashboard_screen.DashboardContract.View is injected at
          [[** redacted**].app.App_HiltComponents.SingletonC] [** redacted**].ui.dashboard_screen.DashboardPresenter.view
      [** redacted**].ui.dashboard_screen.DashboardPresenter is injected at
          [[** redacted**].app.App_HiltComponents.SingletonC] [** redacted**].di.mvp.DashboardPresenterModule.bindDashboardPresenter(presenter)
      [** redacted**].ui.dashboard_screen.DashboardContract.Presenter is injected at
          [[** redacted**].App_HiltComponents.FragmentC] [** redacted**].ui.dashboard_screen.DashboardFragment.presenter
      [** redacted**].ui.dashboard_screen.DashboardFragment is injected at
          [[** redacted**].app.App_HiltComponents.FragmentC] [** redacted**].ui.dashboard_screen.DashboardFragment_GeneratedInjector.injectDashboardFragment([** redacted**].ui.dashboard_screen.DashboardFragment) [[** redacted**].app.App_HiltComponents.SingletonC → [** redacted**].app.App_HiltComponents.ActivityRetainedC → [** redacted**].app.App_HiltComponents.ActivityC → [** redacted**].app.App_HiltComponents.FragmentC]

Now, debugging this behaviour, I notice that the error goes away if I either avoid injecting the presenter in the fragment, or if I avoid injecting the view/fragment in the presenter. Technically, I guess I could pass one of the references as a parameter during some part of an init-block but:

A) It negates part of the purpose of the DI

B) I don't want to do this in every fragment throughout the app.

It's probably something super-obvious that I'm missing, and in a world of "oceans of time", I'd want to get rid of the "MVC"-setup completely and replace it with viewmodels, but that is not feasible. The existing codebase worked fine when we had only Dagger.

I'm not a fan of the chosen design pattern, and I'm unsure if the pattern is applicable. Can someone enlighten me on what is incorrect in the config/setup, or if there is some conceptual issue with the mvc-setup that can be fixed?

0 Answers
Related