https://kotlinlang.org logo
#android
Title
# android
k

Kulwinder Singh

08/16/2019, 6:08 AM
Hi, What is best way to detect "No Internet connection" using Retrofit + Coroutines, currently i have added interceptor
Copy code
private val realInterceptor = Interceptor { chain ->
        if (!NetworkConnection.getInstance().isConnected) {
            throw NoInternetException()
        }
	/..../
    }
but to work with this i have to add
try/catch
block on every request, there are so many requests. Below is sample of how i'm handling each network request->
Copy code
val response = apiService.getItems().toNetworkResult()
        when (response) {
            is NetworkResult.Success -> onDataLoaded(response.data)
            is NetworkResult.Error -> toast(response.errorCode)
        }
and this is how
toNetworkResult
looks like
Copy code
fun <T : Any> Response<T>.toNetworkResult(): NetworkResult<T> {
    val response = this
    if (response.isSuccessful)
        return NetworkResult.Success(response.body()!!)

    val error = GsonUtils.toObject<ErrorResponse>(response.errorBody()!!.string())
    return NetworkResult.Error(response.code(), error)
}
I just want to know if it is possible to handle it inside
toNetworkResult()
or any other similar solution i can implement, where i don't have to add
try/catch
on every request
g

gildor

08/16/2019, 6:16 AM
Copy code
if (!NetworkConnection.getInstance().isConnected) {
            throw NoInternetException()
        }
this approach is just wrong IMO
k

Kulwinder Singh

08/16/2019, 6:17 AM
then what is best way to handle this case?
g

gildor

08/16/2019, 6:18 AM
add try/catch or function that handles this on every request and react according to the case
some error should be visible to user, some shouldn’t
l

louiscad

08/16/2019, 6:19 AM
Network can break at any time, in which case you'll get an
IOException
subclass thrown.
g

gildor

08/16/2019, 6:20 AM
Also if you have connection it doesn’t mean you have access to internet
We usually just check type of exception:
Copy code
fun Throwable.isNotConnected() = this is UnknownHostException || cause is UnknownHostException
Copy code
fun Throwable.isTimeout() = this is SocketTimeoutException || cause is SocketTimeoutException
but it used only to handle it in UI, we never change interceptors or request logic
👍 1
Also in general, every exception should be handled explicitly, depending on case, this
isConnected
doesn’t handle anything useful
I see your intent to simplify network logic, but for UI application, where you just cannot throw exception, you have to handle it, especially for Network, which is never stable enough, so if you will think about exception handling as about part of your UI logic, that you always should hanlde it, it makes it not so confusing, at least for me
k

Kulwinder Singh

08/16/2019, 6:36 AM
Ok thanks, i understood your point and i have tried your method and to not repeat
try/catch
boilerplate i have created this high order function and it worked. let me know if it is ok ?
Copy code
inline fun withNetworkCheck(call: () -> Unit) {
        try {
            call()
        } catch (e: Exception) {
            if (e.isNotConnected()) {
                toast("Not connected to internet")
            } else if (e.isTimeout()) {
                toast("Network connection timeout")
            }
        }

    }

    fun Throwable.isNotConnected() = this is UnknownHostException || cause is UnknownHostException
    fun Throwable.isTimeout() = this is SocketTimeoutException || cause is SocketTimeoutException
Used it like this
Copy code
withNetworkCheck {
	val response = apiService.getItems().toNetworkResult()
        when (response) {
            is NetworkResult.Success -> onDataLoaded(response.data)
            is NetworkResult.Error -> toast(response.errorCode)
        }
}
please let me know if my i implementation is ok?
a

ahulyk

08/16/2019, 7:42 AM
you can try something like this:
Copy code
abstract class BaseUseCase<out T, in P> where T : Any {

    abstract suspend fun run(params: P): Result<Failure, T>

    operator fun invoke(
            scope: CoroutineScope,
            params: P,
            onResult: (Result<Failure, T>) -> Unit = {}
    ) {
        scope.launch {
            try {
                withContext(IO) { run(params) }
            } catch (e: Exception) {
                mapFailure(e)
            }
                    .run {
                        onResult(this)
                    }
        }
    }

    private fun mapFailure(e: Exception) =
            when (e) {
                is HttpException -> Failure.ServerError(e)
                is UnknownHostException, is SocketTimeoutException -> Failure.NetworkConnection(e)
                else -> Failure.FeatureFailure(e)
            }
                    .run {
                        Timber.w(e)
                        Result.Failure(this)
                    }


}
k

Kulwinder Singh

08/16/2019, 2:47 PM
@ahulyk it looks new thing to me, can you please let me know how to use it
s

Saran Akkaraviwat

08/22/2019, 4:34 AM
@jitrapon
5 Views