Hi guys, I need some help with koin.
# koin
a
Hi guys, I need some help with koin.
1
In current situation I have the following
@KoinViewModel
class AViewModel(
@InjectedParam holderA: HolderA,
@InjectedParam holderB: HolderB,
@InjectedParam scope: CoroutineScope
): ViewModel(scope) {}
@Factory
class HolderA(
@InjectedParam scope: CoroutineScope
): CoroutineScope by scope {}
@Factory
class HolderB(
@InjectedParam scope: CoroutineScope
): CoroutineScope by scope {}
I create the view model in compose using:
Copy code
fun createAViewModel(): AViewModel {
 val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) 
 val holderA = koinInject<HolderA> { parametersOf(scope) }
 val holderB = koinInject<HolderB> { parametersOf(scope) }
 val vm = koinViewModel<AViewModel> { parametersOf(holderA, holderB, scope) } 
 return vm
}
Now this is quite some manual work to do, and I am very busy (lazy) man, and I was wondering what trick I can pull off to make the creation of the AViewModel easier, more automatic, and with the same effect as in the example, meaning all the deps will share the same CoroutineScope?
I will reply to my own question 😄
Found a solution, which is quite wild
If anyone is interested
Copy code
val vm = scopedKoinViewModel<AViewModel>()

@Factory
fun uiScope(dispatchers: Dispatchers) = CoroutineScope(SupervisorJob() + dispatchers.ui)

@Composable
inline fun <reified T : ViewModel> scopedKoinViewModel(): T {
    val koinScope = currentKoinScope()
    val coroutineScope = koinInject<CoroutineScope>()
    val dependencies = getDependencies<T>(InjectedParam::class)
        .map { clazz -> koinScope.get<Any>(clazz.java) { parametersOf(coroutineScope) } }
        .plus(coroutineScope)
    return koinViewModel<T> { parametersOf(*dependencies.toTypedArray()) }
}

inline fun <reified T : ViewModel> getDependencies(
    annotationClass: KClass<out Annotation>
): List<KClass<*>> =
    T::class.primaryConstructor?.parameters
        ?.filter { it.findAnnotation<Annotation>()?.annotationClass == annotationClass }
        ?.map { parameters -> parameters.type.classifier as? KClass<*> ?: Any::class }
        ?: emptyList()
A small evolution of the same
Copy code
@Composable
inline fun <reified T : ViewModel> scopedKoinViewModel(
    providedParams: List<Any> = emptyList(),
): T {
    val koinScope = currentKoinScope()
    val coroutineScope = koinInject<CoroutineScope>(qualifier = named("main"))
    val dependencies = getInjectedParamDependencies<T>()
        .filterNot { clazz -> providedParams.any { provided -> provided::class == clazz } }
        .map { clazz -> koinScope.get<Any>(clazz) { parametersOf(coroutineScope) } }
        .plus(providedParams)
    return koinViewModel<T> { parametersOf(*dependencies.toTypedArray(), coroutineScope) }
}

inline fun <reified T : ViewModel> getInjectedParamDependencies(
    annotationClass: KClass<out Annotation> = InjectedParam::class,
): List<KClass<*>> =
    T::class.primaryConstructor?.parameters
        ?.filter { it.findAnnotation<Annotation>()?.annotationClass == annotationClass }
        ?.map { parameters -> parameters.type.classifier as? KClass<*> ?: Any::class }
        ?: emptyList()