Search code examples
dependency-injectiondagger-2android-lifecycleandroid-viewmodelviewmodel-savedstate

How to bind N ViewModelAssistedFactory to Activity


This is my DI setup to achieve Lifecycle SavedState logic:

BaseActivityModule:

@Module
abstract class BaseActivityModule<A : AppCompatActivity> {
    @Binds
    abstract fun provideActivity(activity : A): AppCompatActivity

    @Binds
    internal abstract fun bindSavedStateRegistryOwner(activity : A): SavedStateRegistryOwner

    @Module
    companion object {
        @Provides
        @ActivityContext
        fun provideContext(activity: AppCompatActivity): Context = activity
    }
}

SplashActivityModule:

@Module
abstract class SplashActivityModule : BaseActivityModule<SplashActivity>() {

    @Binds
    @IntoMap
    @ViewModelKey(SplashViewModel::class)
    abstract fun bindFactory(factory: SplashViewModel.Factory): ViewModelAssistedFactory<out ViewModel>

    @Module
    companion object{
        @JvmStatic
        @Nullable
        @Provides
        fun provideDefaultArgs(): Bundle? {
            return null
        }
    }
}

ViewModel:

class SplashViewModel @AssistedInject constructor(
    @Assisted stateHandle: SavedStateHandle,
    ...
) : BaseViewModel(stateHandle,...) {

    @AssistedInject.Factory
    interface Factory : ViewModelAssistedFactory<SplashViewModel>
}

Now let's assume I want to add another ViewModel to my Activity, this will fail:

AnotherViewModel:

class AnotherViewModel @AssistedInject constructor(
    @Assisted stateHandle: SavedStateHandle,
    ...
) : BaseViewModel(stateHandle,...) {

    @AssistedInject.Factory
    interface Factory : ViewModelAssistedFactory<AnotherViewModel>
}

SplashActivityModule:

@Module
    abstract class SplashActivityModule : BaseActivityModule<SplashActivity>() {

        @Binds
        @IntoMap
        @ViewModelKey(SplashViewModel::class)
        abstract fun bindFactory(factory: SplashViewModel.Factory): ViewModelAssistedFactory<out ViewModel>

        @Binds
        @IntoMap
        @ViewModelKey(AnotherViewModel::class)
        abstract fun bindAnotherFactory(factory: AnotherViewModel.Factory): ViewModelAssistedFactory<out ViewModel>        

        @Module
        companion object{
            @JvmStatic
            @Nullable
            @Provides
            fun provideDefaultArgs(): Bundle? {
                return null
            }
        }
    }

error: [Dagger/MissingBinding] ....AnotherViewModel cannot be provided without an @Inject constructor or an @Provides-annotated method. public abstract interface ApplicationComponent extends dagger.android.AndroidInjector<...Application> {

How can I make this work?


Solution

  • Ok I found out, only one piece of the puzzle was missing.
    I was creating only the ViewModel received by parameter from the ChildActivity. To be able to use N ViewModels, ChildActivity is responsible for providing them just like the BaseActivity does. So, I just had to add my ViewModel instantiation in the SplashActivity:

    class SplashActivity : BaseActivity<ActivitySplashBinding, SplashViewModel>() {
    
        private val anotherViewModel by lazy { ViewModelProvider(this, viewModelFactory).get(AnotherViewModel::class.java) }
        ...
        binding.anotherViewModel = anotherViewModel    
    }
    

    It works now!