I noticed that in all the examples of working with the network (Retrofit, Ktor, etc.) create sealed classes like this:
sealed interface ApiResponse<out T> {
data class Success<T> : ApiResponse<T>
data class Failure<T> : ApiResponse<T>
}
What is the advantage of this error handling method? After all, we have to pass the ApiResponse
through all layers of the app to the end consumer.
Or copy this class with similar classes for each layer of clean architecture. Which in turn causes the need to map the data and create even more boilerplate code.
It's okay to use exceptions to send errors? And in the end consumer we can use try/catch
or Flow.catch
. Is there any reason why this method is less desirable than using sealed classes (except that I might forget to handle exceptions)?
Those libraries do use exception. Particularly retrofit, it will throw an exception on a network failure. As does the OkHTTP library that RetroFit is built on. So yes, it's ok to send an exception. Whether you use an exception or a result class is a usability decision that you get to make when writing that type of library.
There are a few reasons you may want to use one or the other, and it gets into the details of what you consider a failure and what is not. Take an API hitting www.example.com/myEndpoint?search=id. It may be programmed so that it return a 200 if id exists and 404 if it doesn't (basically using HTTP error codes as they were designed for). Or it may be programmed so that it returns a 200 either way and the success/failure is in the body of the response (probably the most common way to do http errors). So is a 404 an error that should throw an exception? Probably not for the first case, probably yes for the second. Its frequently easier to return a value and let you decide if that value is an error or not. Exceptions would then be reserved for lower level errors, like the connection being lost before the transfer could complete, or the IP address not responding.