6

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?

Nijat Ahmadli
  • 711
  • 6
  • 13
  • Why do you want to create ViewModel instance of interface FooViewModel type? Are you planning to implement this interface in multiple viewmodel? why can't you go for abstract class which extends ViewModel instead of interface? – Raghul Vaikundam Feb 19 '20 at 19:00
  • Why you want to add interface in ViewModel? – Shweta Chauhan Feb 20 '20 at 05:15
  • @VaikundamRaghul Yes, multiple view models are implementing this interface. The reason I don't want to go for abstract is that I am applying Koin to an existing project and don't want change the contract. – Nijat Ahmadli Feb 20 '20 at 09:52

1 Answers1

2

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.

Nijat Ahmadli
  • 711
  • 6
  • 13