Search code examples
androidrealmretrofit2rx-java2android-mvp

catch the response return in MVP with repository pattern


I'm facing problems: I have been creating a login process that will return a response. If the login is true, it will give response like this.

{   
    "error": false,   "message": "Logged In Successfully",   
    "user": {
        "username": "administrator",
        "email": "[email protected]",
        "first_name": "USER",
        "last_name": "ADMIN"   
    },   
    "menu": [
       {
         "name": "MENU A",
         "module": "menu_a",
         "controller": "",
         "parent_module": "",
         "status": ""
       },
       {
          "name": "MENU B",
          "module": "menu_b",
          "controller": " ",
          "parent_module": "",
          "status": ""
       }   
    ],   
    "privilege": [
       {
           "module": "menu_a"
       },
       {
           "module": "menu_b"
       }   
    ] 
}

Then when it false, it will return response like this.

{   "error": true,   "message": "Incorrect Login",   "user": {
    "username": "none",
    "email": "none",
    "first_name": "none",
    "last_name": "none",
    "company": "none",
    "phone": "none",
    "gender": "none",
    "place_of_birth": "none",
    "date_of_birth": "none",
    "religion": "none",
    "photo": "none"   },   "menu": "none",   "privilege": "none" }

This is my code:

public interface AppDataSource {   

     Single<LoginResponse> attemptLogin(Login login);

     Single<LoginResponse> saveUserData(LoginResponse response);

}

public class AppRemoteDataSource implements AppDataSource {

    private final Retrofit retrofit;

    @Inject
    public AppRemoteDataSource(@NonNull Retrofit retrofit) {
        this.retrofit = retrofit;
    }

    @Override
    public Single<LoginResponse> attemptLogin(Login login) {
        return retrofit.create(OmrService.class).loginUser(login);
    }

    @Override
    public Single<Boolean> checkLogin() {
        throw new UnsupportedOperationException();
    }
}

public class AppLocalDataSource implements AppDataSource {

    @NonNull
    private final SharedPreferences sharedPreferences;

    @NonNull
    private final Realm realm;

    @Inject
    public AppLocalDataSource(@NonNull SharedPreferences sharedPreferences, @NonNull Realm realm) {
        this.sharedPreferences = sharedPreferences;
        this.realm = realm;
    }
    @Override
    public Single<Boolean> checkLogin() {
       return Single.create(s -> {
           Boolean stsLogin = sharedPreferences.getBoolean("IS_USER_LOGIN", >false);
           s.onSuccess(stsLogin);
       });
    }
    @Override
    public Single<LoginResponse> saveUserData(LoginResponse response) {
       return Single.create(e -> {
           if(!response.isError()) {
               savePreference(response.getUser());
               saveMenuPrivilege(response.getMenu(), >response.getPrivilege());
           }
           e.onSuccess(response);
       });
    }

}

public class AppRepository implements AppDataSource {

    @NonNull
    private final AppDataSource localDataSource;

    @NonNull
    private final AppDataSource remoteDataSource;

    @Inject
    public AppRepository(@NonNull @Local AppDataSource localDataSource,
                         @NonNull @Remote AppDataSource remoteDataSource) {
        this.localDataSource = localDataSource;
        this.remoteDataSource = remoteDataSource;
    }

    @Override
    public Single<LoginResponse> attemptLogin(Login login) {
        return remoteDataSource.attemptLogin(login)
                .compose(RxUtils.applySingleSchedulers())
                .flatMap(localDataSource::saveUserData);
    }

    @Override
    public Single<Boolean> checkLogin() {
        return localDataSource.checkLogin();
    }

}

public class LoginPresenter implements LoginContract.Presenter {


    private LoginContract.View view;

    private CompositeDisposable compositeDisposable = new CompositeDisposable();

    private AppRepository repository;


    @Inject
    public LoginPresenter(AppRepository repository, LoginContract.View view) {
        this.repository = repository;
        this.view = view;

    }

    private void attemptLogin(Login login) {
        view.showProgressIndicator(true);
        compositeDisposable.add(repository.attemptLogin(login)
                .subscribe(
                        response -> {
                            if(response.isError()) {
                                Log.d("CHECK RESPONSE ", response.getMessage());
                                view.makeSnack(response.getMessage().toString().replace("<p>","").replace("</p>",""));
                            } else {
                                view.showProgressIndicator(false);
                                view.startMainActivity();
                            }
                        } , e -> {
                            Log.d("CHECK RESPONSE ERROR", e.getMessage());
                            view.makeSnack(e.getMessage().toString());
                            view.showProgressIndicator(false);
                        }
                ));
    }
}

The problem is, when I'm testing it false username the response that show in Toast is not "Incorrect Login", instead "com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 268 path $.menu"

I know the menu and privilege is empty when the login is false. But how i can catch the response message "Incorrect login"?, Is there any part of the code that I need to change?

Thanks in advance


Solution

  • The problem is your privilege key.

    On success it is an array: "privilege": [ { "module": "menu_a" }, { "module": "menu_b" } ]

    In failure it is a string: "privilege": "none"

    A simple way to make it work would be to change "none" to []

    The problem is in your JSON deserializer (GSON?) which obviously cannot represent both a String and an Array the same way.

    So if you cannot change the server response, you most likely need to create a custom deserializer that can do the conversion for you. How to do that depends on what is being used to convert the JSON to Java.