Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 286
Nijat Ahmadli Avatar asked Nov 06 '22 09:11

Nijat Ahmadli


1 Answers

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.

like image 76
Nijat Ahmadli Avatar answered Nov 14 '22 21:11

Nijat Ahmadli