Search code examples
androidinterfaceviewmodelkoin

How to provide ViewModel that has interface via Koin


I have the following ViewModel setup:

interface FooViewModel {}

class FooViewModelImpl: ViewModel(), FooViewModel {}

and I want to provide it via Koin like this:

viewModel<FooViewModel> { FooViewModelImpl() }

It doesn't work because Koin expects ViewModel instead of FooViewModel in the definition and I don't want to make my FooViewModel an abstract class that extends from ViewModel.

Is there a way I can do this via Koin?


Solution

  • The only way I got it working was to override Koin extension function and enforce qualifier:

    • Override Koin Module extension function to drop ViewModel from its generics definition:
    inline fun <reified T> Module.customViewModel(
        qualifier: Qualifier? = null,
        noinline definition: Definition<T>
    ): BeanDefinition<T> {
        return viewModel(qualifier ?: named(T::class.java.name), definition = definition)
    }
    
    inline fun <reified T> Module.viewModel(
        qualifier: Qualifier? = null,
        override: Boolean = false,
        noinline definition: Definition<T>
    ): BeanDefinition<T> {
        val beanDefinition = factory(qualifier, override, definition)
        beanDefinition.setIsViewModel()
        return beanDefinition
    }
    
    • Override Koin LifecycleOwner extension functions same way:
    inline fun <reified T> LifecycleOwner.customViewModel(
        qualifier: Qualifier? = null,
        noinline parameters: ParametersDefinition? = null
    ): Lazy<T> = lazy { getViewModel<ViewModel>(qualifier ?: named(T::class.java.name), parameters) as T }
    
    inline fun <reified T> LifecycleOwner.getCoreViewModel(
        qualifier: Qualifier? = null,
        noinline parameters: ParametersDefinition? = null
    ): T = getViewModel<ViewModel>(qualifier ?: named(T::class.java.name), parameters) as T
    

    Then you can provide FooViewModel as

    viewModel<FooViewModel> { FooViewModelImpl() }
    

    and inject it as:

    private val viewModel: FooViewModel by customViewModel()
    // or
    val viewModel: FooViewModel = getCustomViewModel()
    

    Although this is possible I don't think it is a good idea, just wanted to share my findings. The best way would be to use abstract class (which inherits from Android ViewModel) instead of an interface.