Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class keys into map don't work in Kotlin 1.3.30 #1478

Closed
consp1racy opened this issue Apr 12, 2019 · 37 comments
Closed

Class keys into map don't work in Kotlin 1.3.30 #1478

consp1racy opened this issue Apr 12, 2019 · 37 comments

Comments

@consp1racy
Copy link

Setup:

  • AGP 3.3.x
  • Dagger 2.21+
  • Kotlin 1.3.30
  • Gradle 5.3.1

Error:

e: C:\gradle\build\DaggerKotlin\app\tmp\kapt3\stubs\debug\net\xpece\test\daggerkotlin\TheComponent.java:8: error: [Dagger/MissingBinding] java.util.Map<kotlin.reflect.KClass<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public abstract interface TheComponent {
                ^
      java.util.Map<kotlin.reflect.KClass<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
          net.xpece.test.daggerkotlin.DaggerViewModelFactory(creators)
      net.xpece.test.daggerkotlin.DaggerViewModelFactory is injected at
          net.xpece.test.daggerkotlin.ViewModelModule.bindViewModelFactory(impl)
      androidx.lifecycle.ViewModelProvider.Factory is injected at
          net.xpece.test.daggerkotlin.MainActivity.vmf
      net.xpece.test.daggerkotlin.MainActivity is injected at
          net.xpece.test.daggerkotlin.TheComponent.inject(net.xpece.test.daggerkotlin.MainActivity)

Current state:

Injecting a Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>> based on KClass<out ViewModel keys produces an error.

Changing to Map<KClass ... doesn't help.

Expected state:

Injecting a Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>> based on KClass<out ViewModel keys should work as before.

Workaround:

Migrate the class key annotation to Java.

Relevant code:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
@Singleton
class DaggerViewModelFactory @Inject constructor(
    private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
    // ...
}
@Module
abstract class ViewModelModule {

    @Binds
    abstract fun bindViewModelFactory(impl: DaggerViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(ViewModelOne::class)
    abstract fun bindViewModelOne(impl: ViewModelOne): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(ViewModelTwo::class)
    abstract fun bindViewModelTwo(impl: ViewModelTwo): ViewModel
}
@Component(modules = [ViewModelModule::class])
@Singleton
interface TheComponent {
    fun inject(activity: MainActivity)
}
class MainActivity : Activity() {

    protected val component by lazy {
        DaggerTheComponent.create()
    }

    @Inject
    protected lateinit var vmf: ViewModelProvider.Factory

    protected val vm1: ViewModelOne by lazy { vmf.create(ViewModelOne::class.java) }
    protected val vm2: ViewModelTwo by lazy { vmf.create(ViewModelTwo::class.java) }

    override fun onCreate(savedInstanceState: Bundle?) {
        component.inject(this)
        super.onCreate(savedInstanceState)
    }
}

Sample

DaggerKotlin.zip

@NitroG42
Copy link

The issue probably comes from Kotlin as it is the Kotlin update that stopped the code for working.
I made a Kotlin issue just in case : https://youtrack.jetbrains.com/issue/KT-30979 (I was working on my own sample then saw your issue)

sasfmlzr added a commit to sasfmlzr/DvachMovie that referenced this issue Apr 12, 2019
Sonphil added a commit to ApplETS/Notre-Dame-v3 that referenced this issue Apr 13, 2019
Can't update Kotlin version yet because of the following issue: google/dagger#1478
Sonphil added a commit to ApplETS/Notre-Dame-v3 that referenced this issue Apr 14, 2019
* Bump deps versions

Can't update Kotlin version yet because of the following issue: google/dagger#1478

* Move Resource to shared model package

* Call dashboardViewModel.save() in onPause

When it was called in onStop, sometimes, the dashboard wasn't saved.
alteredworlds pushed a commit to alteredworlds/posts that referenced this issue Apr 15, 2019
@FilippoVigani
Copy link

Would you mind expanding on the workaround with an example?

@bleeding182
Copy link

@VeegaP You switch the ViewModelKey from Kotlin to Java

/**
 * Workaround in Java due to Dagger/Kotlin not playing well together as of now
 * https://github.com/google/dagger/issues/1478
 */
@MapKey
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewModelKey {
    Class<? extends ViewModel> value();
}

@FilippoVigani
Copy link

Thank you @bleeding182 :)

@william-reed
Copy link

Should be fixed in kotlin 1.3.31 (yet to be released) according to the kotlin issue linked above

@vladkarpman
Copy link

I still have the same issue. @bleeding182 Workaround doesn't work for me.

@consp1racy
Copy link
Author

@vladkarpman post your error and your exact workaround code

@vladkarpman
Copy link

vladkarpman commented Apr 23, 2019

Setup:
Dagger version = 2.22.1
Kotlin version = 1.3.30
Gradle plugin version = 3.4.0
Gradle version = 5.1.1

I am using workaround which was suggested @bleeding182.
It's simply class which is written on Java instead of Kotlin:

import androidx.lifecycle.ViewModel;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import dagger.MapKey;

@MapKey
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewModelKey {
    Class<? extends ViewModel> value();
}

This is my ViewModelFactory:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton

@Singleton
class ViewModelFactory @Inject constructor(
        private val viewModels: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) :
        ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return viewModels[modelClass]?.get() as T
    }
}

Below is my ViewModelModule (nothing special) :

@Module
abstract class ViewModelModule {

    @Binds
    abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(DiscoverViewModel::class)
    abstract fun bindDiscoveryViewModel(discoverViewModel: DiscoverViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(LoginViewModel::class)
    abstract fun bindLoginViewModel(loginViewModel: LoginViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(OnBoardingViewModel::class)
    abstract fun bindOnBoardingViewModel(onBoardingViewModel: OnBoardingViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(ProfileViewModel::class)
    abstract fun bindProfileViewModel(profileViewModel: ProfileViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(ProfileCityViewModel::class)
    abstract fun bindProfileCityViewModel(profileCityViewModel: ProfileCityViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(ProfileDetailViewModel::class)
    abstract fun bindProfileDetailViewModel(profileDetailViewModel: ProfileDetailViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(ProfileLanguageViewModel::class)
    abstract fun bindProfileLanguageViewModel(profileLanguageViewModel: ProfileLanguageViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(MyQuestViewModel::class)
    abstract fun bindMyQuestViewModel(myQuestViewModel: MyQuestViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(TasksViewModel::class)
    abstract fun bindTasksViewModel(tasksViewModel: TasksViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(QuestDetailViewModel::class)
    abstract fun bindQuestDetailViewModel(questDetailViewModel: QuestDetailViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(QuestFinishViewModel::class)
    abstract fun bindQuestFinishViewModel(finishViewModel: QuestFinishViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(QuestsViewModel::class)
    abstract fun bindQuestsViewModel(questsViewModel: QuestsViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    abstract fun bindMainViewModel(mainViewModel: MainViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(BuyPromoCodeViewModel::class)
    abstract fun bindBuyPromoCodeViewModel(buyPromoCodeViewModel: BuyPromoCodeViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(FactViewModel::class)
    abstract fun bindFactViewModel(factViewModel: FactViewModel): ViewModel
}

And this is error which i am facing:

/Users/vladislavkarpman/StudioProjects/StreetGames/app/build/tmp/kapt3/stubs/debug/com/streetgames/common/di/application/ApplicationComponent.java:8: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.

@mikehc
Copy link

mikehc commented Apr 23, 2019

@vladkarpman list of things you need to check:

  1. You don't have another file with ViewModelKey defined in Kotlin.
  2. Make sure you are using the java annotations and not kotlin's. Use this package: import java.lang.annotation.*;
  3. As you are not posting the imports, just make sure everything else is using the Java version of ViewModelKey.

@vladkarpman
Copy link

@mikehc i checked everything from provided list, still the same error :(

@mikehc
Copy link

mikehc commented Apr 23, 2019

@vladkarpman post the rest of the other files, including imports. Maybe someone else has a better idea.

@DDihanov
Copy link

@vladkarpman switch back to the old Kotlin version until this gets fixed.

Which imports are you using in the factory class?

@vladkarpman
Copy link

vladkarpman commented Apr 24, 2019

@DDihanov added imports inside ViewModelFactory

@DDihanov
Copy link

@vladkarpman Yup everything is fine on your end. I don't know why it keeps crashing. I "fixed" this issue by downgrading to org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.20 and using the Kotlin ViewModelKey class.

@vladkarpman
Copy link

@DDihanov something went totally wrong, now if i even downgrade Kotling version, i still have this issue. Before everything just worked. Of course, I tried to clean project.
Maybe it's because of new Android Studio, i am using 3.4...

@neworld
Copy link

neworld commented Apr 24, 2019

Sometimes a build cache of kapt doesn't track deleted kotlin files. Probably you have to not only clean build dir but delete build cache as well.

@NitroG42
Copy link

Please try to run ./gradlew cleanBuildCache in your project, then remove your build folder, then try again.

@nicbell
Copy link

nicbell commented Apr 24, 2019

I can confirm, downgrading Kotlin fixes this..

@vladkarpman
Copy link

vladkarpman commented Apr 24, 2019

I reset my branch to previous commit and investigated a bit more and found out that cause of my issue was gradle version 5.1.1 in mix with latest Kotlin version.
Once i changed Gradle version to 4.10.1 and plugin version 3.3.2 (BTW, Gradle plugin version 3.4 only works for gradle version 5.1.1), everything started to work, of course, only with version of Kotlin 1.3.21.

So, robust solution to this issue is following:

  • Kotlin version: 1.3.21
  • Gradle version: 4.10.1
  • Gradle plugin version: 3.3.2

Will wait for Koltin&Gradle fixes :)

@william-reed
Copy link

This is now fixed in kotlin 1.3.31 https://github.com/JetBrains/kotlin/blob/1.3.30/ChangeLog.md

@cbedoy
Copy link

cbedoy commented Oct 21, 2019

I'm having the same issue on

kotlin_version = '1.3.41'

@NezSpencer
Copy link

same

@sinadalvand
Copy link

I'm having the same issue on

kotlin_version = '1.3.41'

same

@radeshf
Copy link

radeshf commented Feb 12, 2020

same in kotlin 1.3.50

@aldefy
Copy link

aldefy commented Feb 24, 2020

Facing this with 1.3.41 , will check with latest - x.50 and x.60 and see if it resolves

@react1ve
Copy link

@aldefy Same issue in x.50, x.60 and x.61

@aldefy
Copy link

aldefy commented Feb 24, 2020

what are you guys trying at the moment ?
Do i write the provides func replacing abstract Binds ?

@Icyrockton
Copy link

1.3.70 same issue

@tatocaster
Copy link

1.3.71 same 🤔

@vincent-paing
Copy link

Still happening with 1.3.71
Dagger 2.72
AGP 4.0-beta04
Gradle 6.1.1

Changing to java doesn't work. Why is this issue closed, it doesn't seem to be resolved yet

@consp1racy
Copy link
Author

To anyone still complaining, care to provide a sample repro project? Thank you.

@SimonStefan
Copy link

I can confirm the Java workaround [1] works fine with
Dagger 2.21 && 2.27
Gradle 5.4.1 && 6.1.1 && 6.3
Kotlin 1.3.70 && 1.3.71

[1] #1478 (comment)

@vincent-paing
Copy link

I clear my gradle cache and rerun and the java solution is working now

@felipezad
Copy link

I tried all of these and none of the above worked for me

dagger_version = ‘2.27'
kotlin_version = ‘1.3.72'
build:gradle:3.6.3'

@nikolaysmirnoff
Copy link

#1540
Adding @JvmSuppressWildcards solved the problem.
private val providers: Map<Class, @JvmSuppressWildcards Provider>

@hsm59
Copy link

hsm59 commented Jul 23, 2020

Tried the solutions provided in this thread, yet facing the same issue.

dagger = 2.15
kotiln version = 1.3.72
build.gradle = 4.0.1

@anshul-gmp
Copy link

ext.kotlin_version = "1.4.0"
ext.daggerVersion = '2.25.4'
build.gradle = "4.0.1"

Still facing the issue. Anyone found any solution ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests