Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@fnk0
Created January 14, 2020 19:15
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 fnk0/5311ac8a82f28e49437dc610ee6dd446 to your computer and use it in GitHub Desktop.
Save fnk0/5311ac8a82f28e49437dc610ee6dd446 to your computer and use it in GitHub Desktop.
Parent View Model with Contract Interface
import androidx.annotation.MainThread
import androidx.fragment.app.Fragment
import kotlin.reflect.KClass
interface ViewModelContractProvider {
fun <T : Any> provideViewModelContract(clazz: KClass<T>): T
val supportedContracts: Set<KClass<*>>
}
/**
* Recursively searches parent fragments for it's ViewModelContractProvider
* If no ViewModelContractProvider is found on parent fragments it will retrieve it from the Activity
* If there is no ViewModelContractProvider in the scope of this fragment an IllegalArgumentException will be thrown
*/
fun <T : Any> Fragment.findViewModelByContract(clazz: KClass<T>): T {
if (supportsViewModelContract(clazz)) {
return (this as ViewModelContractProvider).provideViewModelContract<T>(clazz)
}
// Note the version of fragment that arch components is using does not have the method
// requireParentFragment() so we store into a val
val parent = parentFragment
if (parent != null) {
return parent.findViewModelByContract<T>(clazz)
}
if (activity?.supportsViewModelContract(clazz) == true) {
return (requireActivity() as ViewModelContractProvider).provideViewModelContract<T>(clazz)
}
throw IllegalArgumentException("There is no ViewModelContractProvider associated with $this")
}
@MainThread
inline fun <reified T : Any> Fragment.viewModelContract(): Lazy<T> {
return lazy(LazyThreadSafetyMode.NONE) {
findViewModelByContract<T>(T::class)
}
}
private fun <T : Any> Any.supportsViewModelContract(clazz: KClass<T>): Boolean {
if (this is ViewModelContractProvider) {
return supportedContracts.contains(clazz)
}
return false
}
class SomeActivity : AppCompatActivity(): ViewModelContractProvider {
override fun <T: Any> provideViewModelContract(): T {
@Suppress("UNCHECKED_CAST") val nullableVm = viewModel as? T
checkNotNull(nullableVm) {
"$viewModel does not implement required Contract"
}
return nullableVm
}
override val supportedContracts: Set<KClass<*>> = setOf(SomeViewModelContract::class)
}
class SomeActivityViewModel : ViewModel(): SomeViewModelContract {}
class SomeFragmentViewModel : ViewModel() {}
interface SomeFragmentViewModelContract {
}
class SomeFragment : Fragment() {
val myViewModel by viewModels<SomeFragmentViewModel>()
val contractViewModel by viewModelContract<SomeFragmentViewModelContract>()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment