Reputation: 19824
I am learning Kotlin and Dagger 2 simultaneously by attempting to convert some of Mindorks advanced MVP sample to Kotlin but am having Dagger2 compile issues. I am swimming in classes here but am very close! Don't mind the untidiness, I intend to comb over each class once it compiles. If something is missing please let me know. The error comes down to my presenter class not being correctly injected into the activity. The error reads as follows:
e: D:\_Dev\repo\app\build\tmp\kapt3\stubs\debug\com\xxx\di\component\ActivityComponent.java:8: error: com.xxx.login.LoginMVP.Presenter<com.xxx.login.LoginMVP.View,? extends com.xxx.login.LoginMVP.Interactor> cannot be provided without an @Provides- or @Produces-annotated method.
e:
e: public abstract void inject(@org.jetbrains.annotations.NotNull()
e: ^
e: com.xxx.login.LoginMVP.Presenter<com.xxx.login.LoginMVP.View,? extends com.xxx.login.LoginMVP.Interactor> is injected at
e: com.xxx.login.LoginActivity.presenter
e: com.xxx.login.LoginActivity is injected at
e: com.xxx.di.component.ActivityComponent.inject(activity)
e: java.lang.IllegalStateException: failed to analyze: org.jetbrains.kotlin.kapt3.diagnostic.KaptError: Error while annotation processing
EDIT:
Here is a repo with the failing code, built with the latest canary build of Android Studio
BaseActivity.kt
abstract class BaseActivity : AppCompatActivity(), MvpView {
val activityComponent: ActivityComponent by lazy {
DaggerActivityComponent.builder()
.applicationComponent((application as App).applicationComponent)
.activityModule(ActivityModule(this))
.build()
}
}
BasePresenter.kt
open class BasePresenter<V : MvpView, out I: MvpInteractor>
@Inject constructor(private val mvpInteractor: I) : MvpPresenter<V, I> {
private var mvpView: V? = null
override fun onAttach(mvpView: V) {
this.mvpView = mvpView
}
override fun onDetach() {
mvpView = null
}
override fun getMvpView(): V? {
return mvpView
}
override fun getInteractor(): I {
return mvpInteractor
}
}
MvpPresenter.kt (MvpView and MvpInteractor are basic empty interfaces)
interface MvpPresenter<V: MvpView, out I: MvpInteractor> {
fun onAttach(mvpView: V)
fun onDetach()
fun getMvpView(): V?
fun getInteractor(): I
}
App.kt
class App: Application() {
lateinit var applicationComponent: ApplicationComponent
override fun onCreate() {
super.onCreate()
applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(ApplicationModule(this)).build()
applicationComponent.inject(this)
}
fun getComponent(): ApplicationComponent {
return applicationComponent
}
fun setComponent(applicationComponent: ApplicationComponent) {
this.applicationComponent = applicationComponent
}
}
ApplicationComponent.kt
@Singleton
@Component(modules = arrayOf(ApplicationModule::class))
interface ApplicationComponent {
fun inject(app: App)
@ApplicationContext fun context(): Context
fun application(): Application
//Pref helper
//Api helper
}
ApplicationModule.kt
@Module
class ApplicationModule(val application: Application) {
@Provides
@ApplicationContext
fun provideContext(): Context = application
@Provides
fun provideApplication(): Application = application
//Provide api helper
//Provide pref helper
//Provide api key etc.
}
ActivityModule.kt
@Module
class ActivityModule(val activity: AppCompatActivity) {
@Provides
fun provideContext(): Context = activity
@Provides
fun provideActivity(): AppCompatActivity = activity
@Provides
fun provideLoginPresenter(presenter: LoginPresenter<LoginMVP.View, LoginMVP.Interactor>):
LoginMVP.Presenter<LoginMVP.View, LoginMVP.Interactor> {
return presenter
}
@Provides
fun provideLoginMvpInteractor(interactor: LoginInteractor):
LoginMVP.Interactor {
return interactor
}
}
ActivityComponent.kt
@PerActivity
@Component(dependencies = arrayOf(ApplicationComponent::class), modules = arrayOf(ActivityModule::class))
interface ActivityComponent {
fun inject(activity: LoginActivity)
}
LoginActivity.kt
class LoginActivity : BaseActivity(), LoaderCallbacks<Cursor>, LoginMVP.View {
@Inject lateinit var presenter: LoginMVP.Presenter<LoginMVP.View, LoginMVP.Interactor>
private var authTask: UserLoginTask? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
activityComponent.inject(this)
email_sign_in_button.setOnClickListener { presenter.onServerLoginClick(email.text.toString(), password.text.toString()) }
presenter.onAttach(this)
}
}
LoginMVP.kt
interface LoginMVP {
interface Interactor : MvpInteractor {
}
@PerActivity
interface Presenter<V : LoginMVP.View, out I : LoginMVP.Interactor>
: MvpPresenter<V, I> {
fun onServerLoginClick(email: String, password: String)
}
interface View : MvpView {
fun openMainActivity()
}
}
Upvotes: 5
Views: 1125
Reputation: 1474
It's not a full answer but pretty close.
Problem is inout
modifier. With this modifier Dagger try inject
com.xxx.login.LoginMVP.Presenter<com.xxx.login.LoginMVP.View,? extends com.xxx.login.LoginMVP.Interactor>
but you provide only
com.xxx.login.LoginMVP.Presenter<com.xxx.login.LoginMVP.View, com.xxx.login.LoginMVP.Interactor>
(in function provideLoginPresenter
).
So if remove all out
modifies (from Presenter
, BasePresenter
, LoginPresenter
) it starts compile and work.
I'm not sure why Dagger try inject wrong type or cannot understand that it is same type. It looks like bug while annotation processing. So the simplest solution - don't use out
modifier
with Dagger.
Upvotes: 3