Skip to content

Instantly share code, notes, and snippets.

@AChep
Created December 6, 2019 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AChep/dff06b498f35e01003fd2e928862985d to your computer and use it in GitHub Desktop.
Save AChep/dff06b498f35e01003fd2e928862985d to your computer and use it in GitHub Desktop.
Helper methods to retrieve an instance of ViewModel via Kodein.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import org.kodein.di.*
import org.kodein.di.generic.bind
import org.kodein.di.generic.factory
import org.kodein.di.generic.instance
import org.kodein.di.generic.provider
class ViewModelFactory(private val injector: LazyKodein) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T =
injector.direct.instance<ViewModel>(tag = modelClass.simpleName) as T
}
data class ViewModelParams(
val owner: ViewModelStoreOwner,
val params: DefinitionParameters
)
inline fun <C, reified T : Any, reified R : ViewModel> Kodein.BindBuilder.WithContext<C>.viewModel(
noinline creator: DKodeinAware.(DefinitionParameters) -> R
) = factory { viewModelParams: ViewModelParams ->
// Local kodein is used to hide
// actual ViewModel binding
val localLazyKodein = Kodein.lazy {
extend(this@factory.kodein)
bind<ViewModel>(R::class.java.simpleName) with provider {
creator(viewModelParams.params)
}
}
ViewModelProvider(
viewModelParams.owner,
ViewModelFactory(localLazyKodein)
).get(R::class.java) as T
}
/**
* Returns a kodein property with a ViewModel injected
* into it.
*/
inline fun <T, reified Property : Any> T.viewModels(vararg params: Any?): KodeinProperty<Property> where
T : KodeinAware,
T : ViewModelStoreOwner = run {
val argument = ViewModelParams(this, DefinitionParameters(params))
instance(arg = argument)
}
@Suppress("UNCHECKED_CAST")
class DefinitionParameters(
private vararg val values: Any?
) {
init {
if (values.size > 5)
error(
"""
Sorry, because of the limitations of Kotlin language, you can not
pass more that 5 arguments. Please use data types instead of multiple
arguments.
""".trimIndent()
)
}
private fun <T> elementAt(i: Int): T =
if (values.size > i) values[i] as T else error("Can't get parameter value #$i from $this")
operator fun <T> component1(): T = elementAt(0)
operator fun <T> component2(): T = elementAt(1)
operator fun <T> component3(): T = elementAt(2)
operator fun <T> component4(): T = elementAt(3)
operator fun <T> component5(): T = elementAt(4)
/**
* get element at given index
* return T
*/
operator fun <T> get(i: Int) = values[i] as T
}
@AChep
Copy link
Author

AChep commented Oct 29, 2020

How to bind the view model:

bind<VM> with viewModel { (p1: P1, p2: P2) -> 
    VMImpl(
        p1 = p1,
        p2 = p2,
        p3 = instance(),
    ) 
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment