How to use the Firebase server timestamp to generate date created? - android

Currently, the Google's version of ServerValue.TIMESTAMP returns {".sv":"timestamp"} which is used as a directive for Firebase to fill that field with the server timestamp once you save the data to the Firebase server.
When you create your data on the client side however, you don't have the actual timestamp to play with yet (ie. use as the creation date). You only will have an access to the timestamp after the initial save and consequent retrieval, which - I imagine - is sometimes too late and not very elegant.
Before Google:
Update: Ignore this section as it is incorrect - I misunderstood the examples. ServerValue.TIMESTAMP always returned the {".sv":"timestamp"}.
As far as I understand in pre-google Firebase there seemed to be a server-generated timestamp available that allowed you to acquire the actual timestamp:
import com.firebase.client.ServerValue;
ServerValue.TIMESTAMP // eg. 1466094046
(ref 1, ref 2)
Questions:
Is such save/retrieval the only way to get the server-generated creation date on my model instances?
If yes can you propose a method of implementing such pattern?
Am I understanding correctly ServerValue.TIMESTAMP has changed with Google's acquisition of Firebase? Update: No, #FrankvanPuffelen replied that nothing's changed during acquisition.
Note:
I'm not considering using new Date() on client side as I've been reading it's not safe, though please share your thoughts if you think different.

When you use the ServerValue.TIMESTAMP constant in a write operation, you're saying that the Firebase Database server should determine the correct timestamp when it executes the write operation.
Let's say we run this code:
ref.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getValue());
}
public void onCancelled(DatabaseError databaseError) { }
});
ref.setValue(ServerValue.TIMESTAMP);
This will execute as follows:
you attach a listener
you write a value with ServerValue.TIMESTAMP
the Firebase client immediate fires a value event with an approximation of the timestamp it will write on the server
your code prints that value
the write operation gets sent to the Firebase servers
the Firebase servers determine the actual timestamp and write the value to the database (assuming no security rules fail)
the Firebase server send the actual timestamp back to the client
the Firebase client raises a value event for the actual value
your code prints that value
If you're using ChildEventListener instead of a ValueEventListener, then the client will call onChildAdded in step 3 and onChildChanged in step 8.
Nothing changed in the way we generate the ServerValue.TIMESTAMP since Firebase joined Google. Code that worked before, will continue to work. That also means that the first answer you linked is a valid way to handle it.

I'm doing it a bit differently.
Solution 1: push() method in POJO
As I don't want to clutter my POJOs with strange getters or properties, I'm just defining a push() method inside my POJOs which looks like this:
/**
* Pushes a new instance to the DB.
*
* #param parentNode `DatabaseReference` to the parent node this object shall be attached to
*/
fun push(parentNode: DatabaseReference) {
parentNode
.push()
.apply {
setValue(this#Pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Then I can simply create an instance of the POJO and call push() on it which properly populates the creation time property.
This definitely makes the POJO a little less plain and involves logic a POJO shouldn't know about. However using #Exclude annotations and/or casts as outlined in some of the responses here also requires knowledge of the storing mechanism.
Solution 2: Helper or DatabaseReference extension (Kotlin)
To overcome this you can of course also just create a pushTask(task: Task) method in a helper or - if using Kotlin - an extension method to e.g. DatabaseReference which could look like this:
fun DatabaseReference.push(pojo: Pojo) {
push()
.apply {
setValue(pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Looking at it now I come to think that I actually like the second approach more (if I have Kotlin at my disposal - I don't like helpers). But this is probably just a matter of taste. ;)

Related

Why addSnapshotListener is called twice on firestore CollectionReference? [duplicate]

My firestore onSnapshot() function is being called twice.
let user = firebase.firestore().collection('users').doc(userID).onSnapshot
({
next: (documentSnapshot: firebase.firestore.DocumentSnapshot) =>
{
this.userArray.push(documentSnapshot as User);
console.log(documentSnapshot);
//here
},
error: (firestoreError: firebase.firestore.FirestoreError) =>
{
console.log(firestoreError);
//here
}
});
I have also tried subscribing like in https://firebase.google.com/docs/firestore/query-data/listen#detach_a_listener by including user() at the //here comment but to no avail.
How can I modify such that the function only executes one time, i.e. push only one user object per time instead of twice.
I don't know if this is related to your question. If one is using
firebase.firestore.FieldValue.serverTimestamp()
to give a document a timestamp, then onSnaphot will fire twice. This seem to be because when you add a new document to your database onSnapshot will fire, but the serverTimestamp has not run yet. After a few milliseconds serverTimestamp will run and update you document => onSnapshot will fire again.
I would like to add a small delay before onSnapshot fires (say 0,5s or so), but I couldn't find the way to do this.
You can also make a server side function for onCreate event, I believe that would solve your problem. Maybe your userArray.push-action would be more suitable to execute in server side.
Update: To learn more about the behavior of serverTimestamp() and why it triggers the listener twice read this article: The secrets of Firestore’s FieldValue.serverTimestamp() — REVEALED!. Also, the official documentation states:
When you perform a write, your listeners will be notified with the new data before the data is sent to the backend.
In the article there are a couple of suggested solutions, one of which is to use the metadata property of the snapshot to find whether the Boolean value of metadata.hasPendingWrites is true (which tells you that the snapshot you’re looking at hasn’t been written to the server yet) or false.
For example, in your case you can check whether hasPendingWrites is false and then push the object:
if ( !documentSnapshot.metadata.hasPendingWrites ){
// This code will only execute once the data has been written to the server
this.userArray.push(documentSnapshot as User);
console.log(documentSnapshot);
}
In a more generic example, the code will look like this:
firestore.collection("MyCollection")
.onSnapshot( snapshot => {
if ( snapshot.metadata.hasPendingWrites ){
// Local changes have not yet been written to the backend
} else {
// Changes have been written to the backend
}
});
Another useful approach, found in the documentation is the following:
If you just want to know when your write has completed, you can listen to the completion callback rather than using hasPendingWrites. In JavaScript, use the Promise returned from your write operation by attaching a .then() callback.
I hope these resources and the various approaches will help anyone trying to figure out a solution.
REFERENCES:
Events for local changes
The hasPendingWrites metadata property
Snapshot Listen Options
If you need a one time response, use the .get() method for a promise.
firebase.firestore().collection('users').doc(userID).get().then(snap => {
this.userArray = [...this.userArray, snap.doc);
});
However, I suggest using AngularFire (totally biased since I maintain the library). It makes handling common Angular + Firebase tasks much easier.

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.

Android Firebase firing onChildChanged on write [duplicate]

Currently, the Google's version of ServerValue.TIMESTAMP returns {".sv":"timestamp"} which is used as a directive for Firebase to fill that field with the server timestamp once you save the data to the Firebase server.
When you create your data on the client side however, you don't have the actual timestamp to play with yet (ie. use as the creation date). You only will have an access to the timestamp after the initial save and consequent retrieval, which - I imagine - is sometimes too late and not very elegant.
Before Google:
Update: Ignore this section as it is incorrect - I misunderstood the examples. ServerValue.TIMESTAMP always returned the {".sv":"timestamp"}.
As far as I understand in pre-google Firebase there seemed to be a server-generated timestamp available that allowed you to acquire the actual timestamp:
import com.firebase.client.ServerValue;
ServerValue.TIMESTAMP // eg. 1466094046
(ref 1, ref 2)
Questions:
Is such save/retrieval the only way to get the server-generated creation date on my model instances?
If yes can you propose a method of implementing such pattern?
Am I understanding correctly ServerValue.TIMESTAMP has changed with Google's acquisition of Firebase? Update: No, #FrankvanPuffelen replied that nothing's changed during acquisition.
Note:
I'm not considering using new Date() on client side as I've been reading it's not safe, though please share your thoughts if you think different.
When you use the ServerValue.TIMESTAMP constant in a write operation, you're saying that the Firebase Database server should determine the correct timestamp when it executes the write operation.
Let's say we run this code:
ref.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getValue());
}
public void onCancelled(DatabaseError databaseError) { }
});
ref.setValue(ServerValue.TIMESTAMP);
This will execute as follows:
you attach a listener
you write a value with ServerValue.TIMESTAMP
the Firebase client immediate fires a value event with an approximation of the timestamp it will write on the server
your code prints that value
the write operation gets sent to the Firebase servers
the Firebase servers determine the actual timestamp and write the value to the database (assuming no security rules fail)
the Firebase server send the actual timestamp back to the client
the Firebase client raises a value event for the actual value
your code prints that value
If you're using ChildEventListener instead of a ValueEventListener, then the client will call onChildAdded in step 3 and onChildChanged in step 8.
Nothing changed in the way we generate the ServerValue.TIMESTAMP since Firebase joined Google. Code that worked before, will continue to work. That also means that the first answer you linked is a valid way to handle it.
I'm doing it a bit differently.
Solution 1: push() method in POJO
As I don't want to clutter my POJOs with strange getters or properties, I'm just defining a push() method inside my POJOs which looks like this:
/**
* Pushes a new instance to the DB.
*
* #param parentNode `DatabaseReference` to the parent node this object shall be attached to
*/
fun push(parentNode: DatabaseReference) {
parentNode
.push()
.apply {
setValue(this#Pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Then I can simply create an instance of the POJO and call push() on it which properly populates the creation time property.
This definitely makes the POJO a little less plain and involves logic a POJO shouldn't know about. However using #Exclude annotations and/or casts as outlined in some of the responses here also requires knowledge of the storing mechanism.
Solution 2: Helper or DatabaseReference extension (Kotlin)
To overcome this you can of course also just create a pushTask(task: Task) method in a helper or - if using Kotlin - an extension method to e.g. DatabaseReference which could look like this:
fun DatabaseReference.push(pojo: Pojo) {
push()
.apply {
setValue(pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Looking at it now I come to think that I actually like the second approach more (if I have Kotlin at my disposal - I don't like helpers). But this is probably just a matter of taste. ;)

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 :)

Firebase onDataChanged fire twice when using ServerValue.TIMESTAMP (Android) [duplicate]

Currently, the Google's version of ServerValue.TIMESTAMP returns {".sv":"timestamp"} which is used as a directive for Firebase to fill that field with the server timestamp once you save the data to the Firebase server.
When you create your data on the client side however, you don't have the actual timestamp to play with yet (ie. use as the creation date). You only will have an access to the timestamp after the initial save and consequent retrieval, which - I imagine - is sometimes too late and not very elegant.
Before Google:
Update: Ignore this section as it is incorrect - I misunderstood the examples. ServerValue.TIMESTAMP always returned the {".sv":"timestamp"}.
As far as I understand in pre-google Firebase there seemed to be a server-generated timestamp available that allowed you to acquire the actual timestamp:
import com.firebase.client.ServerValue;
ServerValue.TIMESTAMP // eg. 1466094046
(ref 1, ref 2)
Questions:
Is such save/retrieval the only way to get the server-generated creation date on my model instances?
If yes can you propose a method of implementing such pattern?
Am I understanding correctly ServerValue.TIMESTAMP has changed with Google's acquisition of Firebase? Update: No, #FrankvanPuffelen replied that nothing's changed during acquisition.
Note:
I'm not considering using new Date() on client side as I've been reading it's not safe, though please share your thoughts if you think different.
When you use the ServerValue.TIMESTAMP constant in a write operation, you're saying that the Firebase Database server should determine the correct timestamp when it executes the write operation.
Let's say we run this code:
ref.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getValue());
}
public void onCancelled(DatabaseError databaseError) { }
});
ref.setValue(ServerValue.TIMESTAMP);
This will execute as follows:
you attach a listener
you write a value with ServerValue.TIMESTAMP
the Firebase client immediate fires a value event with an approximation of the timestamp it will write on the server
your code prints that value
the write operation gets sent to the Firebase servers
the Firebase servers determine the actual timestamp and write the value to the database (assuming no security rules fail)
the Firebase server send the actual timestamp back to the client
the Firebase client raises a value event for the actual value
your code prints that value
If you're using ChildEventListener instead of a ValueEventListener, then the client will call onChildAdded in step 3 and onChildChanged in step 8.
Nothing changed in the way we generate the ServerValue.TIMESTAMP since Firebase joined Google. Code that worked before, will continue to work. That also means that the first answer you linked is a valid way to handle it.
I'm doing it a bit differently.
Solution 1: push() method in POJO
As I don't want to clutter my POJOs with strange getters or properties, I'm just defining a push() method inside my POJOs which looks like this:
/**
* Pushes a new instance to the DB.
*
* #param parentNode `DatabaseReference` to the parent node this object shall be attached to
*/
fun push(parentNode: DatabaseReference) {
parentNode
.push()
.apply {
setValue(this#Pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Then I can simply create an instance of the POJO and call push() on it which properly populates the creation time property.
This definitely makes the POJO a little less plain and involves logic a POJO shouldn't know about. However using #Exclude annotations and/or casts as outlined in some of the responses here also requires knowledge of the storing mechanism.
Solution 2: Helper or DatabaseReference extension (Kotlin)
To overcome this you can of course also just create a pushTask(task: Task) method in a helper or - if using Kotlin - an extension method to e.g. DatabaseReference which could look like this:
fun DatabaseReference.push(pojo: Pojo) {
push()
.apply {
setValue(pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Looking at it now I come to think that I actually like the second approach more (if I have Kotlin at my disposal - I don't like helpers). But this is probably just a matter of taste. ;)

Categories

Resources