Skip to content

Instantly share code, notes, and snippets.

@marcellogalhardo
Last active August 6, 2021 13:47
Show Gist options
  • Save marcellogalhardo/a8db9827d8d3d8be4ded568d1b286093 to your computer and use it in GitHub Desktop.
Save marcellogalhardo/a8db9827d8d3d8be4ded568d1b286093 to your computer and use it in GitHub Desktop.
A ViewModel that supports assisted injection.
package dev.marcellogalhardo.viewmodel
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.savedstate.SavedStateRegistryOwner
import kotlin.reflect.KClass
private typealias ViewModelInstantiate<VM> = (AssistedViewModelContext) -> VM
/**
* An utility function that creates generic a [ViewModelProvider.Factory] connected to the [SavedStateRegistryOwner],
* where [instantiate] can be used to create a [ViewModel] using Assisted Injection.
*
* @see [Assisted Injection](https://dagger.dev/dev-guide/assisted-injection.html)
*/
@Suppress("FunctionName")
fun <VM : ViewModel> AssistedViewModelFactory(
owner: SavedStateRegistryOwner,
defaultArgs: Bundle?,
instantiate: ViewModelInstantiate<VM>,
): ViewModelProvider.Factory {
return object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
val context = AssistedViewModelContext(key, modelClass.kotlin, handle)
return instantiate(context) as T
}
}
}
/**
* @see AssistedViewModelFactory
*/
@Suppress("FunctionName")
fun <VM : ViewModel> AssistedViewModelFactory(
activity: ComponentActivity,
instantiate: ViewModelInstantiate<VM>,
): ViewModelProvider.Factory = AssistedViewModelFactory(
owner = activity,
defaultArgs = activity.intent?.extras ?: bundleOf(),
instantiate = instantiate,
)
/**
* @see AssistedViewModelFactory
*/
@Suppress("FunctionName")
fun <VM : ViewModel> AssistedViewModelFactory(
fragment: Fragment,
instantiate: ViewModelInstantiate<VM>,
): ViewModelProvider.Factory = AssistedViewModelFactory(
owner = fragment,
defaultArgs = fragment.arguments,
instantiate = instantiate,
)
data class AssistedViewModelContext internal constructor(
val key: String,
val modelClass: KClass<out ViewModel>,
val handle: SavedStateHandle,
)
/**
* Like [ComponentActivity.viewModels] but allowing assisted injection on the
* created view model. For convenience, [viewModelProducer] has access to a
* [SavedStateHandle] to handling process death.
*
* @see [ComponentActivity.viewModels]
*/
inline fun <reified VM : ViewModel> ComponentActivity.assistedViewModels(
noinline defaultArgs: () -> Bundle? = { intent?.extras },
noinline instantiate: ViewModelInstantiate<VM>,
): Lazy<VM> = viewModels {
AssistedViewModelFactory(owner = this, defaultArgs(), instantiate)
}
/**
* Like [Fragment.viewModels] but allowing assisted injection on the
* created view model. For convenience, [viewModelProducer] has access to a
* [SavedStateHandle] to handling process death.
*
* @see [Fragment.viewModels]
*/
inline fun <reified VM : ViewModel> Fragment.assistedViewModels(
noinline owner: () -> Fragment = { this },
noinline defaultArgs: () -> Bundle? = { owner().arguments },
noinline instantiate: ViewModelInstantiate<VM>,
): Lazy<VM> = viewModels(ownerProducer = { owner() }) {
AssistedViewModelFactory(owner(), defaultArgs(), instantiate)
}
/**
* Like [Fragment.activityViewModels] but allowing assisted injection on the
* created view model. For convenience, [viewModelProducer] has access to a
* [SavedStateHandle] to handling process death.
*
* @see [Fragment.activityViewModels]
*/
inline fun <reified VM : ViewModel> Fragment.assistedActivityViewModels(
noinline owner: () -> ComponentActivity = { requireActivity() },
noinline defaultArgs: () -> Bundle? = { owner().intent?.extras },
noinline instantiate: ViewModelInstantiate<VM>,
): Lazy<VM> = activityViewModels {
AssistedViewModelFactory(owner(), defaultArgs(), instantiate)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment