free geoip
53

Kotlin Retrofit Error Handling Best Practices

Handling errors properly when using Retrofit in Kotlin is crucial for delivering a reliable and user-friendly Android application. Whether you’re…

Handling errors properly when using Retrofit in Kotlin is crucial for delivering a reliable and user-friendly Android application. Whether you’re dealing with network failures, parsing errors, or API-specific error responses, following a structured error-handling pattern helps ensure your app remains robust and user-friendly.

Kotlin Retrofit error handling

Retrofit is one of the most widely used HTTP clients for Android and provides a simple way to make network requests. However, beginners often overlook comprehensive error handling, which leads to unexpected crashes or poor user experience.

Below are best practices for error handling in Kotlin using Retrofit, including complete sample code divided by class and interface.

1. Use a Sealed Class for API Result

// ApiResult.kt
sealed class ApiResult<out T> {
    data class Success<out T>(val data: T): ApiResult<T>()
    data class Error(val exception: Throwable): ApiResult<Nothing>()
}

2. Retrofit Service Interface

// ApiService.kt
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Int): Response<User>
}

3. API Call Wrapper in RepositoryAPI Call Wrapper in Repository

// UserRepository.kt
class UserRepository(private val apiService: ApiService) {

    suspend fun getUserById(id: Int): ApiResult<User> {
        return try {
            val response = apiService.getUser(id)
            if (response.isSuccessful) {
                response.body()?.let {
                    ApiResult.Success(it)
                } ?: ApiResult.Error(Exception("Empty body"))
            } else {
                ApiResult.Error(Exception("HTTP ${response.code()} ${response.message()}"))
            }
        } catch (e: IOException) {
            ApiResult.Error(Exception("Network Error: ${e.localizedMessage}"))
        } catch (e: Exception) {
            ApiResult.Error(e)
        }
    }
}

4. ViewModel Usage

// UserViewModel.kt
class UserViewModel(private val repository: UserRepository): ViewModel() {

    private val _userLiveData = MutableLiveData<ApiResult<User>>()
    val userLiveData: LiveData<ApiResult<User>> = _userLiveData

    fun fetchUser(id: Int) {
        viewModelScope.launch {
            val result = repository.getUserById(id)
            _userLiveData.postValue(result)
        }
    }
}

5. UI Layer Observation

// In Activity or Fragment
viewModel.userLiveData.observe(viewLifecycleOwner) { result ->
    when(result) {
        is ApiResult.Success -> {
            // Show user data
        }
        is ApiResult.Error -> {
            // Show error message
            Toast.makeText(context, result.exception.message, Toast.LENGTH_SHORT).show()
        }
    }
}

Best Practice Summary

  • Always catch IOException and general Exception.
  • Use Response.isSuccessful to check HTTP success.
  • Return a consistent result with a sealed class.
  • Show meaningful errors to users with fallback messages.

For a more advanced approach, consider implementing Retrofit’s custom call adapter.

By applying these patterns, you improve app resilience, make testing easier, and provide a smoother user experience.

rysasahrial

Leave a Reply

Your email address will not be published. Required fields are marked *