kingston
kingston

Reputation: 11419

Can't Provide Qualified String with Dagger2

I'm trying to follow the sample here

I have already used Dagger2 with the AndroidInjector successfully but now I was experimenting the new DaggerApplication and DaggerAppCompatActivity.

I get the error:

Error:(5, 1) error: [dagger.android.AndroidInjector.inject(T)] java.lang.String cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.

I don't think that the new Dagger classes are causing the issue. If I remove the @DeviceModel in BuildModule.kt the code compiles.

Any suggestion?

Here the code:

The AppComponent.kt

@Component(modules = arrayOf(
        AndroidSupportInjectionModule::class,
        AppModule::class,
        MainActivitySubComponent.MainActivityModule::class
))
interface AppComponent : AndroidInjector<App> {
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<App>()
}

The AppModule.kt

@Module(subcomponents = arrayOf(MainActivitySubComponent::class))
class AppModule {

}

The BuildModule.kt

@Module
class BuildModule {
    @Provides
    @DeviceModel
    fun provideModel(): String {
        return MODEL
    }
}

The DeviceModel.kt

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class DeviceModel

The MainActivitySubComponent.kt

@Subcomponent(modules = arrayOf(BuildModule::class))
interface MainActivitySubComponent : AndroidInjector<MainActivity> {

    @Subcomponent.Builder
    abstract class Builder : AndroidInjector.Builder<MainActivity>()

    @Module(subcomponents = arrayOf(MainActivitySubComponent::class))
    abstract class MainActivityModule {
        @Binds
        @IntoMap
        @ActivityKey(MainActivity::class)
        internal abstract fun bind(builder: MainActivitySubComponent.Builder): AndroidInjector.Factory<out Activity>
    }
}

The *MainActivity.kt**

class MainActivity : DaggerAppCompatActivity() {
    @Inject
    @DeviceModel
    lateinit var model: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(model, model);
    }
}

The App.kt

class App : DaggerApplication() {

    override fun applicationInjector(): AndroidInjector<App> {
        return DaggerAppComponent.builder().create(this)
    }
}

Upvotes: 0

Views: 438

Answers (1)

dewey
dewey

Reputation: 312

When using Dagger with Kotlin objects, we have to consider how Kotlin actually creates properties in the JVM.

@Inject
@DeviceModel
lateinit var model: String

With a property like this, Kotlin compiles three Java elements: a private backing field, a getter, and a setter (due to var rather than val). With no further clues as to your intent, Kotlin places your DeviceModel annotation on the getter.

The solution is to specify that the field is to be annotated with @field:[annotation].

@Inject
@field:DeviceModel
lateinit var model: String

You may also need to use @JvmField to make the backing field public for Dagger, which would preclude using lateinit, and of course, require you to initialize the String.

@Inject
@field:DeviceModel
@JvmField
var model: String

Update: I'm not sure why, but in my testing, the field was private, which is why I suggested @JvmField. According to the Kotlin documentation, the field should be created with the same visibility as the setter.


Note that in general, Kotlin is smart enough to apply custom annotations with @Target(AnnotationTarget.FIELD) to the backing field, but this would prevent you from also using it on the fun providesModel

Upvotes: 2

Related Questions