How to provide ViewModel that has interface via Koin
Asked Answered
R

1

7

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?

Rampant answered 19/2, 2020 at 17:47 Comment(3)
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?Drawer
Why you want to add interface in ViewModel?Provender
@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.Rampant
R
3

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.

Rampant answered 20/3, 2020 at 23:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.