Skip to content

Instantly share code, notes, and snippets.

@quentin41500
Last active June 8, 2021 12:28
Show Gist options
  • Save quentin41500/f93978d97f5008cde387db19ce5e2ef6 to your computer and use it in GitHub Desktop.
Save quentin41500/f93978d97f5008cde387db19ce5e2ef6 to your computer and use it in GitHub Desktop.
Using Sealed class and LiveData to handle network request through the repository layer.
/**
* Observable manager for saving the [Cart]'s resource information.
*/
class CartManager : LiveData<Resource<Cart?>>() {
init {
value = Success(null)
}
/**
* Set the [Cart] value and notifies observers.
*/
internal fun set(cart: Cart) {
postValue(Success(cart))
}
/**
* Clear any information from the device.
*/
internal fun clear() {
postValue(Success(null))
}
/**
* Signals that the resource information is being retrieved from network.
*/
internal fun loading() {
postValue(Loading())
}
/**
* Signals that an error occurred when trying to fetch the resource information.
*/
internal fun error(t: Throwable) {
postValue(Failure(t))
}
}
/**
* [Cart] repository class in charge of providing api for getting and updating a [Cart] data.
*/
class CartRepository(private val service: RetrofitApi, private val cartManager: CartManager) {
fun getCartResource(): LiveData<Resource<Cart?>> = cartManager
/**
* Get a [Cart].
*/
suspend fun get() {
withContext(Dispatchers.IO) {
try {
cartManager.loading()
val cart = service.getCart().await()
cartManager.set(cart)
} catch (e: Exception) {
cartManager.error(e)
}
}
}
/**
* Add a [Cart] shipping information.
*/
suspend fun addShippingAddress(shipping: ShippingAddress) {
withContext(Dispatchers.IO) {
try {
cartManager.loading()
val cart = service.addShippingAddress(shipping).await()
cartManager.set(cart)
} catch (e: Exception) {
cartManager.error(e)
}
}
}
}
class CheckoutActivity : AppCompatActivity() {
private lateinit var vm: CheckoutViewModel
override fun onCreate(savedInstanceState: Bundle?) {
// Initialization
...
vm.cart.observe(this, Observer { resource ->
when (resource) {
is Loading -> showLoading()
is Success -> {
displayCart(resource.data) // Do something with your data.
hideLoading()
}
is Failure -> {
showError(resource.throwable) // Do something with your error.
hideLoading()
}
})
}
}
class CheckoutViewModel(repo: CartRepository) : ViewModel() {
/**
* Live data for [Cart] information.
*/
val cart: LiveData<Resource<Cart?>> = repo.getCartResource()
}
/**
* Represents a network bound resource. Each subclass represents the resource's state:
* - [Loading]: the resource is being retrieved from network.
* - [Success]: the resource has been retrieved (available in [Success.data] field)
* - [Failure]: the resource retrieving has failed (throwable available in [Failure.throwable] field)
*/
sealed class Resource<out T> {
class Loading<out T> : Resource<T>()
data class Success<out T>(val data: T) : Resource<T>()
data class Failure<out T>(val throwable: Throwable) : Resource<T>()
}
@whoyawn
Copy link

whoyawn commented Sep 1, 2020

What if you're integrating a database layer? Would we still be observing the resource in the UI if you want to fetched cached objects?

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