I want to be able to fetch data from Firebase server only and if there is no internet connection it should show a failure message.
Source source = Source.SERVER;
firebaseFirestore.collection("stock").document(pid).get(source).addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
String prod_id = document.getString("pid");
}
}
});
I tried doing the above but it still loads cached results if there is no internet.
Please help.
I want to be able to fetch data from firebase server only
This is exactly what your code does. By specifying Source.SERVER, you are telling Firestore to return only data from the server.
if there is no internet connection it should show a failure message.
onFailure() fires only when there is a problem in reading documents, for example, when you don't have permission to access them. In other words, when Firebase server rejects the read operation due to the permissions that are set the in security rules. onFailure() will never fire when there is no internet connection on user device and it makes sense since you need to access the data while offline. This cannot be considered a failure.
I tried doing the above but it still loads cached results if there is no internet.
That's normal behavior. The Firestore SDK caches a copy of the Cloud Firestore data that your app is actively using, so your app can access the data when the device is offline.
Edit:
According to #FrankvanPuffelen comment, in the official documentation is also mentioned that:
public static final Source SERVER
Causes Cloud Firestore to avoid the cache, generating an error if the server cannot be reached. Note that the cache will still be updated if the server request succeeds. Also note that latency-compensation still takes effect, so any pending write operations will be visible in the returned data (merged into the server-provided data).
Related
I'm messing around with Cloud Firestore.
I would like to simply get a callback when reading from DB fails, so I can show a dialog to the user about he has no internet connection. Of course, this would need sophisticated exception handling, but first things first, I would like to just simply get a callback when the app cannot reach the server.
HOWEVER, whenever I test my application with an emulator which has no internet connection, I still get successful callbacks.
This is the log:
Logging_: onSuccess
Logging_: onComplete
Logging_: Task was successful without an internet connection, how?
How is it possible? Am I thinking right that Cloud Firestore is simply not available for this use case since it was built to provide cached data and aggressive syncing in order to provide a seamless user experience even when there is no internet connection?
I would just need a way to just KNOW whether the DB is reachable. (a.k.a - Is there an internet connection problem?)
Code is really simple, it just tries to reach for the current account's characters.
db.collection("users")
.document(accountId)
.collection("characters")
.get()
.addOnCanceledListener(new OnCanceledListener() {
#Override
public void onCanceled() {
Log.i("Logging_", "onCanceled");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.i("Logging_", "onFailure");
}
})
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
Log.i("Logging_", "onSuccess");
}
})
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
Log.i("Logging_", "onComplete");
if (task.isSuccessful()) {
Log.i("Logging_", "Task was successful without internet connection, how?");
} else {
Log.i("Logging_", "Task wasn't successful.");
}
}
});
I would like to simply get a callback when reading from DB fails, so I can show a dialog to the user about whether he has no internet connection.
The Firestore SDK doesn't throw an error when there is no internet connection, and it makes sense since Firestore is designed to work offline. Behind the scenes, Firestore SDK tries to reconnect until the devices regain connectivity. So not having an internet connection cannot be considered a failure. If you want to check for internet connectivity, the following answer might help:
How to verify if user has network access and show a pop-up alert when there isn't
Please notice that Firestore has a built-in mechanism that can help know when an error occurs. So the failure callback occurs when Firestore servers reject the request due to a security rule issue.
There is a solution in which you can force the retrieval of data only from the cache or from the server. Here is the official documentation regarding source options:
https://firebase.google.com/docs/firestore/query-data/get-data#source_options
Firestore has built in caching that is enabled by default for reading from a database on Apple and Android devices. If you want to disable being able to read the cached data, you can do something like this:
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(false)
.build();
db.setFirestoreSettings(settings);
I think what you may want to do instead is listen to network events in Android which would allow you to update the user if they try to perform an action while there is no network available.
This might be a bug. I have logged the tracking info here on GitHub
Firestore call onComplete etc events only online. How do know about write status offline.
db.collection("col").document(id).set(obj)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
//Ok finish activity <----
finish();
} else {
Toast toast = Toast.makeText(getApplicationContext(),
"Error try again", Toast.LENGTH_SHORT);
toast.show();
}
}
});
The code above doesn't works offline, and i can write multiple times. onComplete will not called until i connect to internet. Ho do i implement this feature(closing activity when success).
onComplete will not be called until I connect to the internet.
That's normal behavior. The OnCompleteListener is called only when the data has been written or rejected by the Firebase servers.
A listener will never fire for local write operations. If the local write operation were to fail, the client will raise a regular exception. The Firestore client is designed to continue working even if offline. So writing some data to the database while offline will never produce an error.
Ho do I implement this feature(closing activity when success).
There is no way you can add a completion listener to know when the data is written to the cache and this is because this operation is happening instantly. There is nothing you should worry about in this situation.
What you can in such cases is to check the internet connection. If you are offline, it means that all data is added to the local cache.
Offline writes are considered to be written immediately. There is no completion status for that.
You'll notice that if you have a listener set up for the document that's being written, that listener will trigger immediately, while offline, with the updated document values. You can tell from the document snapshot delivered to the listener if the write has completed or not. If you call snapshot.getMetadata().hasPendingWrites(), it will tell you whether or not the updated document has actually been sent. This is the best possible information you can receive about the status of offline writes.
I read when on Android in offline mode the onComplete() method will never return unless the device goes online so data can be persisted to the backen at Firestore.
When I'm in offline mode and do this get() I notice that it return with what I think must be local data since I have the setPersistenceEnabled(true)
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
}
});
I just have to ask if this is correct? If so is there some flag I can check if the data is online/offline data?
Sometimes doing this get() it does not return just hangs there forever, maybe that has to do with my device getting warm after heavy debugging.
Unlike in Firebase Realtime database where to enable offline persistence you should have used first, the following line of code:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
In Cloud Firestore, for Android and iOS, offline persistence is enabled by default. So, there is no need to use: setPersistenceEnabled(true).
When you are offline and you are using a get() call, the result will be from the cached copy of the Cloud Firestore data that your app is actively using.
To check if the data is from cache or from Firestore servers, you can use the following line of code:
String source = querySnapshot.getMetadata().isFromCache() ? "Local Cache" : "Firebase Server";
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()
I a using firebase and found and issue that firebase not send error for timeout or if not able to connect to server. In that case we are unable to provide correct information to user what the issue is.
Firebase developers must handle this very common use-case. Did anyone encounter this issue?
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot data) {
}
#Override
public void onCancelled(FirebaseError arg0) {
}
Utilize .info/connected to monitor connection state. Firebase works while offline and your onCancelled event is not going to be fired because it is still waiting for the connection to be restored so the message can be delivered.
Firebase is a real-time sync platform. You cannot keep data in sync without any internet access (how will local and remote be reconciled?). So you need to utilize disk persistence (in beta on iOS) or at least have an initial connection to get things moving. Check out offline capabilities for details on all of these topics.