Koin: Manually providing the ViewModelProvider to `by viewModel()`

Created on 30 Aug 2018  路  13Comments  路  Source: InsertKoinIO/koin

Is your feature request related to a problem? Please describe.

I maintain a little library that adds ViewModel support from AAC to Conductor, and recently got this question: https://github.com/miquelbeltran/conductor-viewmodel/issues/1

Have you used Koin at all to inject your ViewModels?

I tried and found an issue: Koin relies on the parent Activity or the Fragment to obtain the ViewModelProvider. However, my Controller class (a replacement for Fragments by Conductor) also is a ViewModelProvider.

This is the method/line that causes the problem: https://github.com/InsertKoinIO/koin/blob/f71200780dd2018e5fdd6292b0a02772396117bd/koin-android-architecture/src/main/java/org/koin/android/architecture/ext/LifecycleOwnerExt.kt#L166

Describe the solution you'd like

A solution would be having an optional paramater in viewModel() and getViewModel(), being able to pass the ViewModelProvider by parameter.

So I can call: val myViewModel by viewModel(viewModelProvider = getMyViewModelProvider())

And the getViewModelByClass will use that provided ViewModelProvider instead.

Describe alternatives you've considered

The alternative is to not to use the ViewModel Koin extension and inject the ViewModelFactories instead.

Target Koin project

this is an improvement for koin-android-architecture


Thanks for considering! I would be very happy to implement it and provide a PR with the solution if you think it is worth it. I am happy user of Koin and as well Conductor, and would like to see them working together. :-)

android accepted

Most helpful comment

included in 2.0

All 13 comments

Hello @arnaudgiuliani, any news about this feature? Thank you!

Yeah, good to ping it.

included in 2.0

wow! thank you!

@arnaudgiuliani one question, could you provide an information how to use the feature in 2.0.0?

sorry, false alert.

ViewModelProvider is generated by Koin: https://github.com/InsertKoinIO/koin/blob/master/koin-projects/koin-android-viewmodel/src/main/java/org/koin/android/viewmodel/ViewModelResolution.kt#L33

The need above is to replace this part then?

Ah, as I understand now, we can provide the ViewModelParameters.from as any custom ViewModelProvider we would like to use.

So we can do now:

val myViewModel: MyViewModel by viewModel(ViewModelParameters(from = { myCustomViewModelProvider })) // more or less

correct?

on Fragment yes. On Activity, you have to dig with more inner API: fun <T : ViewModel> LifecycleOwner.resolveViewModelInstance(parameters: ViewModelParameters<T>)

In ViewModelParameters you can specify the ViewModelStoreOwnerDefinition which is a function to define ViewModelStoreOwner

Should it currently work?
I got this error:
"Feature1Controller@3ae75b - Is not a FragmentActivity nor a Fragment neither a valid ViewModelStoreOwner"

class Feature1Controller: BaseController() {

    override val vm: Feature1ViewModel by viewModel { parametersOf(ViewModelParameters(owner = this, from = { this }, clazz = Feature1Controller::class)) }

    ...
}


abstract class BaseController(): Controller(), LifecycleOwner, ViewModelStoreOwner, KoinComponent {

    abstract val vm: BaseViewModel

    ...
}

class Feature1ViewModel: BaseViewModel() {

    ...
}

Documentation will be updated.

@arnaudgiuliani in the mean time, could you provide a quick snippet here on how it should work? :)

ViewModel API has been refined to make this API better.

ViewModelParameter is now clearly simpler to use:

class ViewModelParameter<T : Any>(
    val clazz: KClass<T>,
    val qualifier: Qualifier? = null,
    val parameters: ParametersDefinition? = null,
    val viewModelStore: ViewModelStore
)

The API offers a simple way to access from "assisted" params to full open:

inline fun <reified T : ViewModel> Scope.viewModel(
    owner: LifecycleOwner,
    qualifier: Qualifier? = null,
    noinline parameters: ParametersDefinition? = null
): Lazy<T>

inline fun <reified T : ViewModel> Scope.getViewModel(
    owner: LifecycleOwner,
    qualifier: Qualifier? = null,
    noinline parameters: ParametersDefinition? = null
): T

fun <T : ViewModel> Scope.getViewModel(
    owner: LifecycleOwner,
    clazz: KClass<T>,
    qualifier: Qualifier? = null,
    parameters: ParametersDefinition? = null
): T

fun <T : ViewModel> Scope.getViewModel(viewModelParameters: ViewModelParameter<T>): T

For AndroidX, we add the SavedStateRegistryOwner property as below:

class ViewModelParameter<T : Any>(
    val clazz: KClass<T>,
    val qualifier: Qualifier? = null,
    val parameters: ParametersDefinition? = null,
    val viewModelStore: ViewModelStore,
    val stateRegistryOwner: SavedStateRegistryOwner? = null
)

This will allow to use the Koin AbstractSavedStateViewModelFactory directly.

Check https://github.com/InsertKoinIO/koin/tree/master/koin-projects/koin-androidx-viewmodel/src/main/java/org/koin/androidx/viewmodel

Hi, is there a working example I can take a look into? Thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

LukasAnda picture LukasAnda  路  3Comments

TedHoryczun picture TedHoryczun  路  4Comments

caleb-allen picture caleb-allen  路  4Comments

fmobus picture fmobus  路  4Comments

erikhuizinga picture erikhuizinga  路  3Comments