Interview frequency: …
Short answer
Using a sealed interface or Result<T> that represents success with data or failure with an error type, forcing the consumer to handle all scenarios.
Explanation
Instead of letting IOException and HttpException be thrown randomly and reach the UI, the repository wraps network calls in a safe block. This turns common network errors (such as no internet, a timeout, or a 404/500 error) into clear states that the UI translates into understandable messages for the user without the app crashing.
Code example
sealed interface NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>
data class Error(val code: Int, val message: String?) : NetworkResult<Nothing>
data class Failure(val throwable: Throwable) : NetworkResult<Nothing>
}
suspend fun <T> safeApiCall(apiCall: suspend () -> T): NetworkResult<T> {
return try {
NetworkResult.Success(apiCall())
} catch (e: HttpException) {
NetworkResult.Error(e.code(), e.message())
} catch (e: IOException) {
NetworkResult.Failure(e)
}
// CancellationException is NOT caught, so coroutine cancellation keeps working
}Common mistakes
- Blindly catching Throwable without distinguishing CancellationException, which prevents cancelling the coroutine when the screen is left.