ViewStateStore

Additional

Language
Kotlin
Version
1.4-beta-4 (Jan 16, 2020)
Created
Feb 20, 2019
Updated
Mar 7, 2020
Owner
Davide Giuseppe Farella (4face-studi0)
Contributor
Davide Giuseppe Farella (4face-studi0)
1
Activity
Badge
Generate
Download
Source code

Promotion

This library is created on an idea of Fabio Collini ( https://proandroiddev.com/unidirectional-data-flow-using-coroutines-f5a792bf34e5 ).

ViewStateStore

ViewStateStore wraps a LiveData for deliver ViewStates to the UI.

Supported ViewState types are;

  • Success holds the real data
  • Error holds and error ( which could be a custom class ) with its Throwable and an optiona customMessageRes.
  • Loading
  • None ( default initial value )

Installation

ViewStateStore

implementation( "studio.forface.viewstatestore:viewstatestore:last_version" )

Paging extension

implementation( "studio.forface.viewstatestore:viewstatestore-paging:last_version" )

Minimal usage

Create

class CarsViewModel(val getCars: GetCars): ViewModel() {
    val cars = ViewStateStore<List<Car>>()

    init {
        cars.setLoading()
        viewModelScope.launch {
            runCatching { withContext(IO) { getCars() } }
                .onSuccess { cars::set(it) }
                .onFailure { cars.setError(it) }
        }
    }
}

Get

observe ( with LifecycleOwner )

class CarsFragment: Fragment(), ViewStateFragment {
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        // carsViewModel.cars.observeData { cars -> ... }
        carsViewModel.cars.observe {
            doOnData(::updateCars)
            doOnError(::showError)
            doOnLoading { isLoading -> progressBar.isVisible = isLoading }
        }
    }
}

Iterator ( with CoroutineScope )

class CarsFragment: Fragment(), ViewStateFragment {
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        lifecycleScope.launch {
            // for (viewState in carsViewModel.cars) { ... }
            // for (cars in carsViewModel.cars.data) { ... }
            for ((onData, onError, onLoadingChange) in carsViewModel.cars.composed) {
                onData?let(::updateCars)
                onError?.let(::showError)
                onLoadingChange?let { isLoading -> progressBar.isVisible = isLoading }
            }
        }
    }
}

await ( with CoroutineScope )

class CarsFragment: Fragment(), ViewStateFragment {
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        lifecycleScope.launch {
            val currentOrNextViewState = carsViewModel.cars.await()
            val currentOrNextData = carsViewModel.cars.awaitData()
            val onlyNextViewState = carsViewModel.cars.awaitNext()
            val onlyNextData = carsViewModel.cars.awaitNextData()
        }
    }
}

get ( nullable )

class CarsFragment: Fragment(), ViewStateFragment {
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        val nullableCurrentViewState = carsViewModel.cars.state()
        val nullableCurrentData = carsViewModel.cars.data()
        try {
         val currentViewState = carsViewModel.cars.unsafeState()
         val currentData = carsViewModel.cars.unsafeData()
        } catch(e: KotlinNullPointerException) {
            ...
        }
    }
}

You can set also an

ErrorResolution// CarsViewModelinit { loadCars() } privatefunloadCars() { cars.setLoading() viewModelScope.launch { runCatching { withContext(IO) { getCars() } } .onSuccess(cars::setData) .onFailure { cars.setError(it, ::loadCars) } } } } // CarsFragmentoverridefunonActivityCreated( savedInstanceState:Bundle? ) { carsViewModel.cars.observe { ... doOnError(::showError) } } funshowError(error:ViewState.Error) { Snackbar.make( coordinatorLayout, error.getMessage(requireContext()), Snackbar.LENGTH_SHORT ).apply { if (error.hasResolution()) setAction("Retry") { error.resolve() } } show() } } }


It's also possible to

lock the ViewStateStore for make it be mutable only from a ViewStateStoreScopeclassCarsViewModel( valgetCars:GetCars ):ViewModel(), ViewStateStoreScope { val cars =ViewStateStore<List<Car>>().lock // Locking the ViewStateStore } classCarsFragment:Fragment(), ViewStateFragment { overridefunonActivityCreated( savedInstanceState:Bundle? ) { carsViewModel.cars.postLoading() // Does NOT compile, LockedViewStateStore.postLoading not resolved } }