Parse-SDK-Android icon indicating copy to clipboard operation
Parse-SDK-Android copied to clipboard

ParseUser user.saveEventually() always create new user in database

Open andj207 opened this issue 6 years ago • 8 comments

ParseUser was newly created every time I use user.saveEventually(). I checked in the ParseDashboard, the Installation object for my device is unique but the "user" field in Installation table always different every time I completely close the app (back pressed, wipe out recently apps) and reopen it. The User table has new row too.

My code:

ParseUser.enableAutomaticUser();
        ParseUser user = ParseUser.getCurrentUser();
        if (user != null) {
            ParseACL.setDefaultACL(new ParseACL(ParseUser.getCurrentUser()), true);
            if (user.get("appLaunch") == null) {
                user.put("appLaunch", 0);
                user.put("notifyAll", true);
            }
            user.saveEventually();  // <-- this line will create new user everytime it run
        }

ParseInstallation installation = ParseInstallation.getCurrentInstallation();
        if (installation != null) {
            String refreshedToken = FirebaseInstanceId.getInstance().getToken();
            if (refreshedToken != null) {
                installation.setDeviceToken(refreshedToken);
            }
            installation.put("user", ParseUser.getCurrentUser());
            installation.saveEventually();
        }

I use latest ParseServer and: implementation "com.parse:parse-android:1.17.3"

andj207 avatar Jul 09 '18 02:07 andj207

I would remove the ParseUser.enableAutomaticUser();, as you are essentially doing what it does in the next lines. That might be the issue, but maybe not. Try that out first.

Jawnnypoo avatar Jul 09 '18 03:07 Jawnnypoo

@Jawnnypoo If ParseUser.enableAutomaticUser(); was removed, ParseUser.getCurrentUser(); always returns null. I need to create anonymous user for use in the app.

andj207 avatar Jul 09 '18 03:07 andj207

Btw, If I use user.saveInBackground(); everything works as expected. There must be a bug with user.saveEventually();.

andj207 avatar Jul 09 '18 03:07 andj207

Also struggling with this, did you encounter any further issues when creating anonymous users with user.saveInBakcground() for example if you call that when there's no internet connection?

AlejandroHCruz avatar Sep 17 '18 15:09 AlejandroHCruz

@AlejandroHCruz yes, I still struggle with this issue, the User collection grows very fast compare to Installation collection. For now I switch to use Installation pointer for "relation" instead.

andj207 avatar Sep 17 '18 15:09 andj207

Gotcha, thanks! user.cryInBackground()

AlejandroHCruz avatar Sep 18 '18 09:09 AlejandroHCruz

So, the issue is that the ParseAnonymousUser is actually not getting saved properly, which results in no user being available when the app starts again. So the usual path of creating a new anon user is followed. The solution that worked for me was like below:

In my Application file

@Override 
public void onCreate() {
     ...
     // Enable automatic creation of ParseAnonymousUser 
     ParseUser.enableAutomaticUser();
    
     // Login the anonymous user so as to create an entry in the Parse's User table
     loginAnonUserIfNeeded() 
}

where loginAnonUserIfNeeded() is another method, which takes care of logging in the ParseAnonymousUser.

void loginAnonUserIfNeeded() {
        // If the current user does not have the object id then it is
        // an anon user, then go ahead and login the user into parse
        if (ParseUser.getCurrentUser() != null
                && ParseUser.getCurrentUser().getObjectId() == null) {
            // call to login only creates the anon user but does not persist it
            ParseAnonymousUtils.logIn((user, e) -> {
                if (e == null) {
                    // The user returned here is ParseUser object which is now stored in remote Parse
                    // server. If the login was not successful this will be null. So we check for the
                    // user object that si returned is not null and login was successful.
                    if (user != null) {
                        // After login, save is required so that it persists across app starts
                        // ⚠️ WARNING: Do not use saveEventually as it is buggy and creates new anon user
                        // every time app is opened
                        user.saveInBackground(e1 -> {
                            if (e1 == null) {
                                //User was saved successfully
                            } else {
                                // Print stacktrace of the error
                                e1.printStackTrace();
                            }

                        });
                    }
                } else {
                    // Print stacktrace of the error
                    e.printStackTrace();
                }
            });
        }
    }

How to test this: Setup OkHttpInterceptor for logging outgoing calls to Parse Server and you can validate that the create user call is made only once.

I hope this helps others who bump into this problem in the future 😄

nisrulz avatar Sep 18 '18 09:09 nisrulz

Great solution! Please take into account that ParseAnonymousUtils.logIn((user, e) -> will return a null ParseUser object if there's no internect connection.

AlejandroHCruz avatar Sep 18 '18 10:09 AlejandroHCruz