MJQZ1347 MJQZ1347 - 3 months ago 106
Swift Question

Firebase: Downloaded timestamp created by FIRServerValue.timestamp() is slightly different than the online version

I noticed that the downloaded timestamps of objects that are created by

FIRServerValue.timestamp()
are slightly different than the online version. My code:

let messagesRef = self.ref.child("messages").child(chatId)
messagesRef
.queryOrderedByChild("timestamp")
.queryLimitedToLast(500)
.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in

guard let object = snapshot.value as? [String: AnyObject] else { return }

let messageId = snapshot.key

if let
senderId = object["senderId"] as? String,
senderName = object["senderName"] as? String,
senderEmail = object["senderEmail"] as? String,
timestamp = object["timestamp"] as? NSTimeInterval {

let date = timestamp.toDate()
let text = object["text"] as? String

print("text: \(text) - timestamp: \(timestamp)")
}
})


Here is a sample output compared to the online value (marked by
->
):

text: Optional("1") - timestamp: 1471596374007.0 -> 1471596374874
text: Optional("2") - timestamp: 1471596375044.0 -> 1471596375324
text: Optional("3") - timestamp: 1471596376157.0 -> 1471596376461
text: Optional("4") - timestamp: 1471596461213.0 -> 1471596463220
text: Optional("5") - timestamp: 1471596542659.0 -> 1471596543307


I sometimes encounter a bug where a messages comes before another message, even though it has been sent after that particular message. I assume it has something to do with this behaviour. When the retrieved timestamps are not exact, messages that are sent close to each other in time, can be ordered differently.

Answer

According to the documentation, when you specify the server timestamp the data sent to the server contains a:

Placeholder value for the number of milliseconds since the Unix epoch.

That placeholder will be replaced with the server's timestamp. However, if you have ChildAdded listeners on the devices that add data, those listeners are notified locally - and that's going to involve replacing the placeholder with a local timestamp. I suspect that's why you are seeing a difference between what's reported via the listener - when a message is written - and what shows up on the console. And that's also why the values that are read match those that are shown in the console.

Firebase likely takes into account the offset between the local and server clocks - as it accounts for this in the generation of keys for pushed data - but you are still going to get a small difference because the data is going to take some time to get to the server.

This local firing of events is also the reason you might sometimes see ChildAdded event firing in an unexpected order. I answered a question regarding unexpected snapshot ordering a few days ago.