Android Parse: Can't update local user - android

I have a custom boolean field called Approved in my custom ParseUser object. We change this field from false to true via a web app. According to the documentation, I should be able to update currentUser after changing the flag with fetchInBackground(), like this:
ParseUser.getCurrentUser().fetchInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject parseObject, ParseException e) {
Log.v("qwer", "fetched User: " + ((CustomUser)parseObject).isApproved());
}
});
But even though the approved flag has been set to true, the result coming back from the server is always false. Unless I logout and login again, at which point currentUser and the respective field is synced with the server.
Why doesn't fetchInBackground() work for the local user, and if I'm using it incorrectly, how do I update the currentUser.

Please see this link, the point is to use pinInBackground and also enable local datastore with Parse.enableLocalDatastore(yourContext); in your Application class.
Update:
Not sure if this will help you, but I do have a similar situation with you:
I do a typical user.put("userVerified", true); then user.saveInBackground to save a boolean that indicates whether user has been verified, then in the next activity I use user.getBoolean("userVerified") to retrieve the flag...Would this be something you might consider?

Related

How to check if Cloud Firestore data is cached? [duplicate]

After deleting data from my Firestore Database, it takes my Android app some time to realize that the data was deleted, and I assume that it's happening due the auto data cache. My app has nothing to do with offline usage and I'd like to disable this feature...
I have added this in my custom Application Class:
import android.app.Application;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
public class ApplicationClass extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseFirestore db=FirebaseFirestore.getInstance();
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(false)
.build();
db.setFirestoreSettings(settings);
}
}
The problem occurs after turning off the internet connection and than turning it back on (while the app is still running, in the background or not)- the Firestore module seems to lose connection to the server, and it makes the opposite operation than the intended one - instead of stop taking data from the cache, it takes data from the cache only.
For example, debugging this code will always show that isFromCache is true and documentSnapshot is empty (even though that on the server side - it's not empty):
usersRef.document(loggedEmail).collection("challenges_received").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
boolean isFromCache=documentSnapshots.getMetadata().isFromCache();
if (!documentSnapshots.isEmpty()) {
}
}
});
Is this normal behavior?
Is there another way to disable the data cache in Cloud Firestore?
EDIT:
Adding: FirebaseFirestore.setLoggingEnabled(flase); (instead of the code above) in the custom Application Class gives the same result.
According to Cloud Firestore 16.0.0 SDK update, there is now a solution to this problem:
You are now able to choose if you would like to fetch your data from the server only, or from the cache only, like this (an example for server only):
DocumentReference documentReference= FirebaseFirestore.getInstance().document("example");
documentReference.get(Source.SERVER).addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
//...
}
});
For cache only, just change the code above to Source.CACHE.
By default, both methods still attempt server and fall back to the cache.
I just ran a few tests in an Android application to see how this works. Because Firestore is currently still in beta release and the product might suffer changes any time, i cannot guarantee that this behaviour will still hold in the future.
db.collection("tests").document("fOpCiqmUjAzjnZimjd5c").get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot documentSnapshot = task.getResult();
System.out.println("isFromCache: " + documentSnapshot.getMetadata().isFromCache());
}
});
Regarding the code, is the same no matter if we're getting the data from the cache or you are connected to the servers.
When I'm online it prints:
isFromCache: false
When I'm offline, it prints:
isFromCache: true
So, for the moment, there is no way to stop the retrieval of the data from the cache while you are not connected to the server, as you cannot force the retrieval of the data from the cache while you're connected to the server.
If instead I use a listener:
db.collection("tests").document("fOpCiqmUjAzjnZimjd5c").addSnapshotListener(new DocumentListenOptions().includeMetadataChanges(), new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
System.out.println("listener.isFromCache: " + documentSnapshot.getMetadata().isFromCache());
}
});
I get two prints when I'm online:
listener.isFromCache: true
listener.isFromCache: false
Firestore is desinged to retrieve data from the chache when the device is permanently offline or while your application temporarily loses its network connection and for the moment you cannot change this behaviour.
As a concusion, an API that does something like this, currently doesn't exist yet.
Edit: Unlike in Firebase, where to enable the offline persistence you need use this line of code:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
In Firestore, for Android and iOS, offline persistence is enabled by default.
Using the above line of code, means that you tell Firebase to create a local (internal) copy of your database so that your app can work even if it temporarily loses its network connection.
In Firestore we find the opposite, to disable persistence, we need to set the PersistenceEnabled option to false. This means that you tell Firestore not to create a local copy of your database on user device, which in term means that you'll not be able to query your database unless your are connected to Firebase servers. So without having a local copy of your database and if beeing disconected, an Exception will be thrown. That's why is a good practice to use the OnFailureListener.
Update (2018-06-13): As also #TalBarda mentioned in his answer this is now possible starting with the 16.0.0 SDK version update. So we can achieve this with the help of the DocumentReference.get(Source source) and Query.get(Source source) methods.
By default, get() attempts to provide up-to-date data when possible by waiting for data from the server, but it may return cached data or fail if you are offline and the server cannot be reached. This behavior can be altered via the Source parameter.
So we can now pass as an argument to the DocumentReference or to the Query the source so we can force the retrieval of data from the server only, chache only or attempt server and fall back to the cache.
So something like this is now possible:
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference docIdRef = db.collection("tests").document("fOpCiqmUjAzjnZimjd5c");
docIdRef.get(Source.SERVER).addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
//Get data from the documentSnapshot object
}
});
In this case, we force the data to be retrieved from the server only. If you want to force the data to be retrieved from the cache only, you should pass as an argument to the get() method, Source.CACHE. More informations here.
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(false)
.build();
dbEventHome.setFirestoreSettings(settings);
By setting this it is fetching from server always.
In Kotlin:
val db:FirebaseFirestore = Firebase.firestore
val settings = firestoreSettings {
isPersistenceEnabled = false
}
db.firestoreSettings = settings
// Enable Firestore logging
FirebaseFirestore.setLoggingEnabled(flase);
// Firestore
mFirestore = FirebaseFirestore.getInstance();
In general: the Firebase client tries to minimize the number of times it downloads data. But it also tries to minimize the amount of memory/disk space it uses.
The exact behavior depends on many things, such as whether the another listener has remained active on that location and whether you're using disk persistence. If you have two listeners for the same (or overlapping) data, updates will only be downloaded once. But if you remove the last listener for a location, the data for that location is removed from the (memory and/or disk) cache.
Without seeing a complete piece of code, it's hard to tell what will happen in your case.
Alternatively: you can check for yourself by enabling Firebase's logging [Firebase setLoggingEnabled:YES];
try this For FireBase DataBase
mDatabase.getReference().keepSynced(false);
FirebaseDatabase.getInstance().setPersistenceEnabled(false);
In Kotlin;
val settings = FirebaseFirestoreSettings.Builder()
with(settings){
isPersistenceEnabled = false
}
Firebase.firestore.firestoreSettings = settings.build()

Firebase Database stops syncing

I have been using Firebase Database in my Android app for almost a year now and it works pretty nice. Unfortunately the data stops being synced to the could after some time. It is just never synced/stored to the cloud. Only local. So when user reinstalls the app, it only contains the data which was stored in the cloud. So to the user it looks like the data was removed, but actually is was never stored. I checked and the data is not visible in the firebase-console. Because it happens after a reinstall I guess it has something to do with the syncing. Users report losing data of about 2-3 months.
I'm using the following singleton helper class. Note I use the setPersistenceEnabled(true) and keepSynced(true).
public class FirebaseHelper{
protected FirebaseHelper(Context c) {
this.c = c.getApplicationContext();
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mAuth = FirebaseAuth.getInstance();
this.userRef = FirebaseDatabase.getInstance().getReference().child(((BuildConfig.DEBUG ? "debug" : "release"))).child("users").child(getUID());
this.userRef.keepSynced(true);
this.path1 = userRef.child("path1");
this.path2 = userRef.child("path2");
this.path3 = userRef.child("path3");
this.path4 = userRef.child("path4");
}
public static FirebaseHelper getInstance(Context c) {
if (instance == null) {
instance = new FirebaseHelper(c);
}
return instance;
}
public String insertObject(MyObject obj) {
DatabaseReference newItem = this.path1.push();
String pushID = newItem.getKey();
obj.id = pushID;
newItem.setValue(obj.getObject());
return pushID;
}
public void updateData(...){}
...other methods
}
What could possibly be the cause of this?
There are only three reasons for this to happen to the best of my knowledge.
1) The method getUID()
Somehow the getUID() method is returning a null or invalid value which leads the data to be stored to somewhere else or it is not getting stored at all.
You are using simply getUid() instead of FirebaseAuth.getInstance().getCurrentUser().getUid(). So it must be a user defined method.
I think your getUid() does something like this.
String getUid() {
try{
return FirebaseAuth.getInstance().getCurrentUser().getUid() ;
} catch (NullPointerException e) {
return null;
}
}
FirebaseAuth.getInstance().getCurrentUser() will return null if cache is cleared. It will lead your data to be lost.
2) Redundant data
Since Marshmallow, data is backed up to the cloud including shared preference. If you are checking shared preference to decide if user is logged in, user will be gone taken inside after reinstalling the app, skipping the login page. But actually he is not logged in which means FirebaseAuth.getInstance().getCurrentUser() returns null and any attempt to access the database will fail (depends on your database rules).
Solution: use FirebaseAuth.getInstance().getCurrentUser()==null to check if user is enabled.
You can alternatively set backup to false. But I prefer first method.
3) An internal bug in Firebase SDK
Unfortunately there is nothing much we can do with it. Try narrowing it down and find a scenario by which the issue can be reproduced and report it to Firebase.
By the way, child(((BuildConfig.DEBUG ? "debug" : "release"))) is really smart. I am going to adopt it.
Well, if your database has been syncing and you have not made any changes to the code whatsoever, it means that this is a firebase error, particularly related to the mobile phone the user is using.
Most developers who use firebase find problems querying the database when certain carriers are used. I have researched into this issue but i have not yet resolved it yet. If you happen to be using mobile data, actions like authenticating a user may not work.
Solution
Use a different internet source to test your code. Try using wifi instead of mobile data while debugging or testing your app.
if I find any helpful work around, I will file it on a firebase project experiencing the problem on open source Lucem
Throwing this out as a guess as well:
At some point you distributed an app where BuildConfig.DEBUG = true, so users that install an updated version "lose" their data. Doesn't explain why other users haven't reported shorter losses though...
The solution would be a data migration, checking which has newer data and then copying the data if DEBUG is newer.

Firebase - Access data without Callback

I want to get the data stored in the DB without being restricted to access it only when there is a data change.
I've seen this post from 2016:
How to access Firebase data without using addValueEventListener
Which suggested to use addValueEventListener.
I've also seen this post:
Accessing data in Firebase databse
Without good answer.
ValueEventListener will trigger the onDataChange only when the database will have a change.
How else can I access the database without something being changed in the database?
For now I will write simple harmless change in order to access the data, but i'm wondering if it's the only way to do it.
Thanks
Of course this is absolutely not true. You can retrieve data whenever you like to.
Firstly I would like to advice you to read this documentation reference.
Secondly I provide you with what you really asked for.
If you read the documentation you will notice that it states the following:
The onDataChange() method in this class is triggered once when the listener is attached and again every time the data changes, including the children.
That means that with this code:
databaseReference.removeEventListener(eventListener);
With that method you would be able to detatch any listener so it only listens once or detatch it whenever you want to.
There is a method for only retrieving data once though.
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "Data retrieved.");
}
...
}
This method will exactly call onDataChange once or respectively onCancelled.

Firebase Remote Config - Initial fetch return local default values

I'm using Firebase Remote Config to fetch remote data and my app needs an up-to-date data from the first launch.
I'm doing a fetch and update in my Application's onCreate():
mFirebaseRemoteConfig.fetch(cacheExpiration)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
mFirebaseRemoteConfig.activateFetched();
}
}
});
And read the value with :
myValue = mFirebaseRemoteConfig.getBoolean(Constants.FIREBASE_REMOTE_MY_VALUE);
The first fetch works well (activateFetched() is successfully triggered), but it returns the remote_config_defaults value and not the published remote config.
The second fetch, even a few seconds later, returns the remote value.
After that, the following fetches are subject to the cacheExpiration rule (which is totally OK).
Any idea why my remote value is not fetched at the first call?
It sounds like you are overlooking the asynchronous nature of fetching the remote parameters. The onComplete() callback fires after a request to the Firebase servers is sent and the reply received. This will take a fraction of a second, maybe more.
If your statement to use the fetched value:
myValue = mFirebaseRemoteConfig.getBoolean(Constants.FIREBASE_REMOTE_MY_VALUE);
follows the call to fetch() and is not in the onComplete() callback, it will execute before the config data has been received. The second call only appears to work because enough time has elapsed for the first call to complete and the data it fetched and activated is present.
The callbacks for Firebase Remote Config have been designed like that, it will return the cached values first. If there is no cached value saved from the server, it will return the value defined in defaults and trigger a remote fetch. The next time it returns it will return the fetched values from the server if it manages to save them.
The way in which Firebase Remote Config decides on a value can be described as follows:
First it checks if there is a cached value that was stored from the server, if there is it uses that and will return that value on the first call.
If there is no cached value, it looks to the defaults defined either programmatically or in the defaults file. (When you call setDefaults())
If there is no value cached from the server, and no value in defaults, it uses the system default for that type.
More info can be found here : https://firebase.google.com/docs/remote-config/
Like #Bob Snyder pointed out, this is because of the async nature of firebase.
So use onCompleteListener like this to fix the issue:
firebaseRemoteConfig.activate().addOnCompleteListener {
//logic to check the remote value
}
One issue that I was running into when fetching the RemoteConfig from an Android device was that we were initially using the method
fetch()
which gave us the same issue where the initial value was always the same as the default. Changing this to
fetchAndActivate()
fixed the issue for us. I assume the difference is that Firebase allows you to fetch the data but not immediately 'activate' it, which presumably is helpful if you want to take some immediate action based on your default values, then activate the remote values and then any logic after that point would be based on the remote values.
Hope this helps someone :)

Update user fields with Parse

How can I update a user field using Parse as backend? Here is the code:
for(ParseUser user : mFriends) {
if (user.get("phoneNumber").toString().equals(result.substring(3))) {
user.increment("field");
user.saveInBackground();
}
}
The problem is that if I use the current user (the user who's using the application) instead of user, it works, but I want to update the field of another user.
You are not allowed to modify objects you do not own unless you set up public ACLs for that object.
Please see Object-Level Access Control and ParseACL for details.
In addition, you should probably look into Relational Data to better model your data. You really should not be modifying a direct field of another User object.

Categories

Resources