androidkotlindagger-2android-mvp

cannot be provided without an @Provides-annotated method Dagger/MissingBinding


Unable to inject presenter to Activity

BookDashboard -> Activity

class BookDashboard : AppCompatActivity(),BookDashboardContract.MvpView{

    @Inject
    lateinit var presenter: BookDashboardContract.Presenter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        presenter.fetchedBooks() 
        // if I pass no parameter to BookPresenter than UninitializedPropertyAccessException: lateinit property presenter has not been initialized
    }

    override fun displayBooks() {
        Toast.makeText(this,"Books Displayed",Toast.LENGTH_LONG).show()
    }

    override fun showProgress() {}
    override fun hideProgress() {}
}

BookDashboardContract

interface BookDashboardContract {

    interface MvpView{
        fun displayBooks()
        fun showProgress()
        fun hideProgress()
    }

    interface Presenter{
        fun fetchedBooks()
    }
}

BookDashboardPresenter If I pass no parameter to below constructor app will run but still presenter is not injected and crashed at presenter.fetchBooks() in BookDashboard -> Activity __ See comments in that code

class BookDashboardPresenter @Inject constructor(val viewContract:BookDashboardContract.MvpView) : BookDashboardContract.Presenter{

    val bookInteractor = BookInteractor(this)

    override fun fetchedBooks() {
        bookInteractor.fetchDataFromServer()
        viewContract.displayBooks()
    }
}

ActivityBuilder

@Module
abstract class ActivityBuilder {

    @ContributesAndroidInjector(modules = {BookDashboardModule.class})
    @ActivityScope
    public abstract BookDashboard bindBookDashboard();
}

BookDashboardModule

@Module
abstract class BookDashboardModule {

    @Binds
    @ActivityScope
    abstract fun presenter(presenter: BookDashboardPresenter): BookDashboardContract.Presenter
}

Second approach I used. But still presenter isn't injected

 @Module
 class BookDashboardModule(val mvpView: BookDashboardContract.MvpView) {

    @Provides
    @ActivityScope
    fun providesCategoryView(): BookDashboardContract.MvpView {
        return this.mvpView
    }

    @Provides
    @ActivityScope
    fun provideBookPresenter(): BookDashboardPresenter {
        return BookDashboardPresenter(mvpView)
    }
}

@Subcomponent.Factory method is missing parameters for required modules or subcomp

@Module(subcomponents = ActivityBuilder_BindBookDashboard.BookDashboardSubcomponent.class)
public abstract class ActivityBuilder_BindBookDashboard {
  private ActivityBuilder_BindBookDashboard() {}

  @Binds
  @IntoMap
  @ClassKey(BookDashboard.class)
  abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory(
      BookDashboardSubcomponent.Factory builder);

  @Subcomponent(modules = BookDashboardModule.class)
  @ActivityScope
  public interface BookDashboardSubcomponent extends AndroidInjector<BookDashboard> {
    @Subcomponent.Factory
    interface Factory extends AndroidInjector.Factory<BookDashboard> {}
  }
}

Edit

As Onik comment I have replace AppCompatActivity with DaggerAppCompatActivity but still unable to inject presenter

MvpView cannot be provided without an @Provides-annotate

/Users/geek/StudioProjects/MyApplication/app/build/tmp/kapt3/stubs/debug/quiz/mania/trivia/mcq/question/di/AppComponent.java:8: error: [Dagger/MissingBinding] quiz.mania.trivia.mcq.question.booksmvp.contract.BookDashboardContract.MvpView cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<quiz.mania.trivia.mcq.question.App> {
                ^
      quiz.mania.trivia.mcq.question.booksmvp.contract.BookDashboardContract.MvpView is injected at
          quiz.mania.trivia.mcq.question.booksmvp.presenter.BookDashboardPresenter(viewContract)
      quiz.mania.trivia.mcq.question.booksmvp.presenter.BookDashboardPresenter is injected at
          quiz.mania.trivia.mcq.question.di.BookDashboardModule.presenter(presenter)
      quiz.mania.trivia.mcq.question.booksmvp.contract.BookDashboardContract.Presenter is injected at
          quiz.mania.trivia.mcq.question.booksmvp.BookDashboard.presenter
      quiz.mania.trivia.mcq.question.booksmvp.BookDashboard is injected at
          dagger.android.AndroidInjector.inject(T) [quiz.mania.trivia.mcq.question.di.AppComponent → quiz.mania.trivia.mcq.question.di.ActivityBuilder_BindBookDashboard.BookDashboardSubcomponent]

AppComponent

@Component(
    modules = [
        AndroidInjectionModule::class, // why it is necessary to use this class for injecting purpose
        AppModule::class,
        ActivityBuilder::class
    ]
)
@Singleton
interface AppComponent : AndroidInjector<App> {

    @Component.Builder
    interface Builder {

        fun addContext(@BindsInstance context: Context): Builder

        fun build(): AppComponent
    }
}

App.kt

class App : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.builder().addContext(this).build()
    }
}

Solution

  • I think there are some mess with the dependency graph. Please change your code like below and try again:

    @Module
     class BookDashboardModule {
    
        @Provides
        @ActivityScope
        fun provideBookPresenter(view: BookDashboard): BookDashboardContract.Presenter = BookDashboardPresenter(view)
    }
    

    ////////

    @Module
    abstract class ActivityBuilder {
    
        @ContributesAndroidInjector(modules = {BookDashboardModule.class})
        @ActivityScope
        abstract fun bind(): BookDashboard
    }
    

    /////////

    class BookDashboardPresenter @Inject constructor(val view: BookDashboardContract.MvpView) : BookDashboardContract.Presenter{
    
        val bookInteractor = BookInteractor(this)
    
        override fun fetchedBooks() {
            bookInteractor.fetchDataFromServer()
            view.displayBooks()
        }
    }