https://kotlinlang.org logo
#kodein
Title
# kodein
k

kluck

12/06/2023, 4:11 PM
Hello, I'm trying to use KodeinDI alongside FlowMVI (https://github.com/respawn-app/flowMVI). In their sample, they provide extension functions for injecting the view model via Koin, and I tried to the best of my capability to reproduce what they made. Here's their code:
Copy code
@Composable
inline fun <reified T : Container<S, I, A>, S : MVIState, I : MVIIntent, A : MVIAction> storeViewModel(
    viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
        "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
    },
    key: String? = null,
    extras: CreationExtras = defaultExtras(viewModelStoreOwner),
    scope: Scope = getKoinScope(),
    noinline parameters: ParametersDefinition? = null,
): StoreViewModel<S, I, A> = getViewModel(qualifier<T>(), viewModelStoreOwner, key, extras, scope, parameters) // -> This is a koin method
What I came up with:
Copy code
@Composable
fun <S : MVIState, I : MVIIntent, A : MVIAction> storeViewModel(
    viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
        "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
    },
    tag: Any? = null,
): StoreViewModel<S, I, A> = with(localDI()) {
    getViewModel(viewModelStoreOwner = viewModelStoreOwner, tag = tag, di = this)
}

@Composable
inline fun <reified T : ViewModel> getViewModel(
    viewModelStoreOwner: ViewModelStoreOwner,
    tag: Any?,
    di: DIAware,
): T = resolveViewModel(T::class, viewModelStoreOwner, tag, di)


inline fun <reified T : ViewModel> resolveViewModel(
    vmClass: KClass<T>,
    viewModelStoreOwner: ViewModelStoreOwner,
    tag: Any?,
    di: DIAware,
): T {
    val factory = di.viewModelFactory(tag)
    val provider = ViewModelProvider(
        store = viewModelStoreOwner.viewModelStore,
        factory = factory,
        defaultCreationExtras = defaultExtras(viewModelStoreOwner),
    )
    return provider[vmClass.java]
}


fun DIAware.viewModelFactory(tag: Any?): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
        di.direct.Instance(type = erased(modelClass), tag = tag)
}
It kind-of works, as I manage to retrieve my first store view model (
val store = storeViewModel<MainState, MainIntent, MainAction>(tag = MAIN_STORE_TAG)
). But as soon as I try injecting a second one, it retrieves the first one… I use tags to distinguish the two, but I guess there is some erasing stuff that I don't get correctly… Any idea what I could change in my code? Thanks!
Hmm, I found a solution, I used
provider[tag, vmClass.java]
instead of
provider[vmClass.java]
. Not sure if it's the best solution though…
k

kluck

12/11/2023, 7:45 AM
Yes I did, that's where I found this usage of
provider[tag, vmClass.java]
, thanks!
r

romainbsl

12/11/2023, 7:49 AM
Cool 👍 ! I’m curious to know your experience with FlowMVI. It seems there are many MVI/Redux implementations available for KMP.
k

kluck

12/11/2023, 8:08 AM
Just testing it for now, but it looks promising, I like the modularity of it by making everything as they call it "plugins". The fact that it's not intertwined with the Android framework makes it easier to test and control. Definitely worth a try!
👍 2
👍🏾 1
4 Views