Reusable mechanism to listen LiveData for specific errors in Android

I have integration with backend made with retrofit. My project architecture is similar to known GithubBrowserSample proposed by Google as example. This means, my ViewModel has some LiveData that can be observed

Any request can throw "401 unauthorized" and I would like to redirect to login fragment. And I can do this when checking details of errors at fragment during observing

    viewModel.user.observe(viewLifecycleOwner, { response ->
        if (response.status == Status.SUCCESS) {
            // do work there
        } else if (response.status == Status.ERROR) {
            // check does this 401 and redirect
        }
    })

But, I have a lot of such places in my app and I need some mechanism for listening of 401 and redirect which can be applied to all places out of the box without checking in every place

Let's say, some wrapper, some utils to reuse check for specific error

Upvotes: 0

Views: 70

Answers (3)

Franz Andel
Franz Andel

Reputation: 1701

If the error you want to handle is specific to 401 Unauthorized, you can implement Authenticator and attach it to your okhttp.

First, create your Authenticator class

class YourAuthenticator : Authenticator {

    // This callback is only called when you got 401 Unauthorized response
    override fun authenticate(route: Route?, response: Response): Request? {
        // Navigate to Login fragment
    }
}

Then, add Authenticator to your okhttp builder

val yourAuthenticator = YourAuthenticator()
val okHttp = OkHttpClient.Builder()
            .authenticator(yourAuthenticator)
            .build()

Upvotes: 1

ChristianB
ChristianB

Reputation: 2690

This might not limit the checking you need to do, but it moves the logic from the Activity into the ViewModel (where it can be easier tested).

In your ViewModel

val user: LiveData<Result<User>> = repository.getUser() // put your actual source of fetching the user here

val needsRedirectToLogin: LiveData<Boolean> = Transformations.map(user) { response ->
    response.code == 401
}

Improvement: You could create an extension function for this, you can reuse in other ViewModels for the same scenario:

/**
 * Returns true when [code] matches the Response code.
 */
fun LiveData<Response<Any>>.isErrorCode(code: Int): LiveData<Boolean> = Transformations.map(this) { response ->
    response.code == code
}

And then use it like:

val needsRedirectToLogin: LiveData<Boolean> = user.isErrorCode(401)

In your Fragment:

viewModel.needsRedirectToLogin.observe(viewLifecycleOwner, { redirectToLoginPage ->
if (redirectToLoginPage) { 
  // do the redirect here.
}

The next step could be having an EventBus, or BroadcastReceiver you could send this event. So that you have only one place to handle this.

There are lots of ways how to go from here (depending on your requirements). But I hope this gives you an idea and will help you.

Upvotes: 1

syed muhammad awais
syed muhammad awais

Reputation: 165

Make a Base class , Create a class which extends the AppCompactActivity means the base class will be a Parent activity and listen for the user.observe there and extends your current activity with the parent activity :

class parentActivity extends AppCompactActivity() {
    onCreate(){
      ////Listener for the observer here 
        this.user.obser{
        } 
    } 
 }

//////////////////////////

class yourActivity extends parentActivity(){

//TODO your code

}

Upvotes: 1

Related Questions