30

A custom object that takes a parameter of (DocumentSnapShot documentsnapShot). also is an inner object from Firebase that retrieves a snapshot and set the values to my custom model also have its argument (DocumentSnapShot documentsnapShot). However, I wish to get the data from Firebase and pass it to my custom argument because mine takes multiple data not only Firebase. And it's not possible to iterate Firestore without an override.

Here's the code:

public UserSettings getUserSettings(DocumentSnapshot documentSnapshot){
    Log.d(TAG, "getUserSettings: retrieving user account settings from firestore");

    DocumentReference mSettings = mFirebaseFirestore.collection("user_account_settings").document(userID);
    mSettings.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            UserAccountSettings settings = documentSnapshot.toObject(UserAccountSettings.class);
            settings.setDisplay_name(documentSnapshot.getString("display_name"));
            settings.setUsername(documentSnapshot.getString("username"));
            settings.setWebsite(documentSnapshot.getString("website"));
            settings.setProfile_photo(documentSnapshot.getString("profile_photo"));
            settings.setPosts(documentSnapshot.getLong("posts"));
            settings.setFollowers(documentSnapshot.getLong("followers"));
            settings.setFollowing(documentSnapshot.getLong("following"));
        }
    });
}
Andreas
  • 2,455
  • 10
  • 21
  • 24
Prolifixs
  • 305
  • 1
  • 3
  • 6

2 Answers2

32

You cannot return something now that hasn't been loaded yet. Firestore loads data asynchronously, since it may take some time for this. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available. If you want to pass settings object to another method, just call that method inside onSuccess() method and pass that object as an argument. So a quick fix would be this:

@Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
    UserAccountSettings settings = documentSnapshot.toObject(UserAccountSettings.class);
    yourMethod(settings);
}

One more thing to mention is that you don't need to set the those values to object that already have them. You are already getting the data from the database as an object.

So remember, onSuccess() method has an asynchronous behaviour, which means that is called even before you are getting the data from your database. If you want to use the settings object outside that method, you need to create your own callback. To achieve this, first you need to create an interface like this:

public interface MyCallback {
    void onCallback(UserAccountSettings settings);
}

Then you need to create a method that is actually getting the data from the database. This method should look like this:

public void readData(MyCallback myCallback) {
    DocumentReference mSettings = mFirebaseFirestore.collection("user_account_settings").document(userID);
    mSettings.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            UserAccountSettings settings = documentSnapshot.toObject(UserAccountSettings.class);
            myCallback.onCallback(settings);

        }
    });
}

In the end just simply call readData() method and pass an instance of the MyCallback interface as an argument wherever you need it like this:

readData(new MyCallback() {
    @Override
    public void onCallback(UserAccountSettings settings) {
        Log.d("TAG", settings.getDisplay_name());
    }
});

This is the only way in which you can use that object of UserAccountSettings class outside onSuccess() method. For more informations, you can take also a look at this video.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • you can add `success` and `fail` in the interface and call them in ail or success cases. – Sambhav Khandelwal Mar 12 '22 at 17:27
  • @Alex Mamo If the controller is calling this method readData(), how do we return the result? We can write it to log, database, but how to return it to the calling method? – user2441441 Mar 31 '23 at 23:32
  • You cannot return the result even if you call readData(). If you understand Kotlin, I think that this [resource](https://medium.com/firebase-tips-tricks/how-to-read-data-from-cloud-firestore-using-get-bf03b6ee4953) will help. Here is the corresponding [repo](https://github.com/alexmamo/CloudFirestore/). – Alex Mamo Apr 01 '23 at 11:17
  • Using Java, but I think same rules about not being able to return value would apply to Java? So consider a user executing the api via swagger or postman. The user gets back a 201 and a url to check the status of the job? Or a 201 and the browser blocks i.e user waits to get back the entire data automatically? - Provided you have your callback defined as a public String onCallback(ClassName obj) – user2441441 Apr 01 '23 at 20:09
  • Provided you have your callback defined as a public String onCallback(ClassName obj) ; And define callback to return the data from the onSuccess method(?) – user2441441 Apr 01 '23 at 20:15
5

Use LiveData as return type and observe the changes of it's value to execute desired operation.

private MutableLiveData<UserAccountSettings> userSettingsMutableLiveData = new MutableLiveData<>();

public MutableLiveData<UserAccountSettings> getUserSettings(DocumentSnapshot documentSnapshot){

    DocumentReference mSettings = mFirebaseFirestore.collection("user_account_settings").document(userID);
    mSettings.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            UserAccountSettings settings = documentSnapshot.toObject(UserAccountSettings.class);
            settings.setDisplay_name(documentSnapshot.getString("display_name"));
            settings.setUsername(documentSnapshot.getString("username"));
            settings.setWebsite(documentSnapshot.getString("website"));
            settings.setProfile_photo(documentSnapshot.getString("profile_photo"));
            settings.setPosts(documentSnapshot.getLong("posts"));
            settings.setFollowers(documentSnapshot.getLong("followers"));
            settings.setFollowing(documentSnapshot.getLong("following"));

            userSettingsMutableLiveData.setValue(settings);
        }
    });

    return userSettingsMutableLiveData;
}

Then from your Activity/Fragment observe the LiveData and inside onChanged do your desired operation.

getUserSettings().observe(this, new Observer<UserAccountSettings>() {
    @Override
    public void onChanged(UserAccountSettings userAccountSettings) {
        //here, do whatever you want on `userAccountSettings`
    }
});
Md. Asaduzzaman
  • 14,963
  • 2
  • 34
  • 46