skydoves / sandwich

🥪 Sandwich is an adaptable and lightweight sealed API library designed for handling API responses and exceptions in Kotlin for Retrofit, Ktor, and Kotlin Multiplatform.

Home Page:https://skydoves.github.io/sandwich/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[ASK] How can I get throw exception as onError/onFailure/onException in ViewModel ?

chanjungkim opened this issue · comments

commented

This is related to this question

In the Repository.

    suspend fun reCheckout(
        orderData: OrderData
    ) = flow{
        var jwt: String? = null

        loginAndRegister(orderData.phoneNumber)
            .suspendOnFailure { emit(this) }
            .suspendMapSuccess {
                val authData = this
                jwt = "jwt ${authData.auth.token}"
                Client.saveAuth(authData)
                cancelOrder(
                    orderData.orderId!!,
                    OrderCancelRequestBody("store", "re-checkout")
                ).getOrThrow()
            }
            .suspendOnFailure { emit(this) }
            .suspendMapSuccess { addMultiCartItem(jwt!!, orderData.multiCartBody!!).getOrThrow() }
            .suspendOnFailure { emit(this) }
            .suspendMapSuccess { checkOut(orderData.checkoutBody!!).getOrThrow() }
            .suspendOnFailure { emit(this) }
            .suspendOnSuccess { emit(this) }
    }

And

In the ViewModel that calls the Repository.

        repository.reCheckout(orderData!!).collectLatest { apiResponse ->
            apiResponse.onSuccess {
                try {
                    val checkoutResponse = data as CheckoutResponseBody
                    orderData!!.updateCheckoutResult(checkoutResponse)
                    _uiState.value = PaymentUiState.Success(orderData!!)
                }catch (e: Exception){
                    e.printStackTrace()
                    sendLog(e.stackTraceToString())
                }
            }.onError {
                sendLog("${this.errorBody?.string()}")
            }.onException {
                sendLog("$exception")
            }
        }

I am using in this way. And when one of the API calls gets error, it throws because it usesgetOrThrow().

public fun <T> ApiResponse<T>.getOrThrow(): T {
  when (this) {
    is ApiResponse.Success -> return data
    is ApiResponse.Failure.Error -> throw RuntimeException(message())
    is ApiResponse.Failure.Exception -> throw exception
  }
}

But I think that would be nice if I can get the response because checking the status code is important to check the server error.

I thought I could get the exception in the ViewModel. Is there anyway to approach this..?

So, I tried to do like this. But it's not working.

try {
    cancelOrder(
        orderData.orderId!!,
        OrderCancelRequestBody("store", "re-checkout")
    ).getOrThrow()
}catch (e: Exception){
    val result = Response.error(500, e.message?.toResponseBody(null))
    emit(ApiResponse.of { result })
}

I also need to dismiss like loading animation or something too.

Hey @chanjungkim, for this check out the example below:

  fun sequential() = flow {
    disneyService.fetchDisneyPosters().apply { emit(this) }
      .suspendMapSuccess {
        disneyService.fetchDisneyPosters1().apply { emit(this) }.getOrThrow()
      }
      .suspendOnSuccess {
        disneyService.fetchDisneyPosters2().apply { emit(this) }.getOrThrow()
      }
  }.catch { emit(ApiResponse.error(it)) }

You can catch the overall exceptions by attaching a catch extension to your flow:

= flow {
    loginAndRegister(orderData.phoneNumber).apply { emit(this) }
      .suspendMapSuccess {
        val jwt = "jwt ${data.auth.token}"
        Client.saveAuth(data)
        cancelOrder(
          orderData.orderId,
          OrderCancelRequestBody("store", "re-checkout")
        ).apply { emit(this) }.getOrThrow()
      }
      .suspendMapSuccess { addMultiCartItem(jwt, orderData.cartItems).apply { emit(this) }.getOrThrow() }
      .suspendMapSuccess { checkOut(orderData.checkoutBody!!).apply { emit(this) }.getOrThrow() }
      .suspendOnSuccess { emit(this) }
 }.catch { emit(ApiResponse.error(it)) }