Skip to content

Instantly share code, notes, and snippets.

@catalinghita8
Created May 23, 2022 19:10
Show Gist options
  • Save catalinghita8/b1dc17d7561c72016b26f8cc15751f8f to your computer and use it in GitHub Desktop.
Save catalinghita8/b1dc17d7561c72016b26f8cc15751f8f to your computer and use it in GitHub Desktop.
// Consumer usage
class MyFragment : Fragment(R.layout.my_fragment) {
private val viewModel: MyViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val consumer = consumeState(viewModel)
}
}
// Holder usage
class MyViewModel : ViewModel(), StateHolder by DefaultStateHolder() {
init {
setError()
setLoading()
}
}
// State holder
interface StateHolder {
fun setError(throwable: Throwable?)
fun setLoading(value: Boolean)
fun clearErrorState()
val errorHolder: LiveData<Error?>
val loadingHolder: LiveData<Boolean>
}
class DefaultStateHolder : StateHolder {
private val internalErrorHolder = MutableLiveData<Error>(null)
private var internalLoadingHolder = MutableLiveData(false)
override fun setError(throwable: Throwable?) {
// set error on `internalErrorHolder`
}
override fun clearErrorState() {
internalErrorHolder.value = null
}
override fun setLoading(value: Boolean) {
internalLoadingHolder.value = value
}
override val errorHolder: LiveData<Error>
get() = internalErrorHolder
override val loadingHolder: LiveData<Boolean>
get() = internalLoadingHolder
}
// State consumer
fun Fragment.consumeState(holder: StateHolder) =
DefaultStateConsumer(this, holder)
class DefaultStateConsumer(val fragment: Fragment, val holder: StateHolder) :
ReadOnlyProperty<Fragment, StateHolder> {
init {
val errorObserver = Observer<Error> { error ->
if (error != null) {
showLoading(false)
showError(error)
}
}
val loadingObserver = Observer<Boolean> {
showLoading(it)
}
val lifecycleCallback = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentViewCreated(
fm: FragmentManager,
f: Fragment,
v: View,
savedInstanceState: Bundle?,
) {
if (f === fragment) {
holder.errorHolder.observe(f.viewLifecycleOwner, errorObserver)
holder.loadingHolder.observe(f.viewLifecycleOwner, loadingObserver)
}
super.onFragmentViewCreated(fm, f, v, savedInstanceState)
}
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
if (f === fragment) {
holder.errorHolder.removeObserver(errorObserver)
holder.loadingHolder.removeObserver(loadingObserver)
fragment.parentFragmentManager.unregisterFragmentLifecycleCallbacks(this)
}
}
}
fragment.parentFragmentManager.registerFragmentLifecycleCallbacks(lifecycleCallback,
false)
}
override fun getValue(thisRef: Fragment, property: KProperty<*>): StateHolder {
val lifecycle = fragment.viewLifecycleOwner.lifecycle
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
throw IllegalStateException("Should not attempt to bind consumer when views are destroyed")
}
return holder
}
fun showError(error: ErrorContainer) {
// show error
}
fun showLoading(visible: Boolean) {
// show loading
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment