Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create negative Firebase timestamp in swift

For an iOS app I am working on, I need to fetch messages in descending order i.e the latest message comes first, followed by second newest message etc.

From looking at other SO answers and research it seems that the best approach for my situation is to create a negative timestamp and then persist that to the database as an extra property to messages.

I will then use queryOrderedByChild('negativeTimestamp') to fetch the messages in a observeSingleEvent and then have a childAdded observer to handle messages which are sent once initial calls are made.

In the firebase documentation it says I can get the server timestamp value from this snippet of code firebase.database.ServerValue.TIMESTAMP

How do I write this for Swift 3?

like image 939
Edward Avatar asked Apr 04 '17 09:04

Edward


2 Answers

First, see the linked answer in the comments. That answer relies on the client to generate a timestamp that's made negative and written to Firebase.

If you want to have Firebase generate a timestamp, that can be done as well with this little snappy Firebase structure and piece of code.

First, let's take a look at the structure

root
  parent_node
    -Y8j8a8jsjd0adas
       time_stamp: -1492030228007
  timestamp: 1492030228007

Next, some code to create and work with that structure:

Define a var we can use within our class that references the Firebase time stamp

let kFirebaseServerValueTimestamp = [".sv":"timestamp"]

and a function that adds an observer to the timestamp node:

func attachObserver() {

    let timestampRef = self.ref.child("timestamp")
    let parentNodeRef = self.ref.child("parent_node")
    var count = 0

    timestampRef.observe(.value, with: { snapshot in

        if snapshot.exists() {
            count += 1
            if count > 1 {
                let ts = snapshot.value as! Int
                let neg_ts = -ts
                let childNodeRef = parentNodeRef.childByAutoId()
                let childRef = childNodeRef.child("time_stamp")
                childRef.setValue(neg_ts)
                count = 0
            }
        }
    })

And a function that writes out a timestamp, therefore causing the observer to fire which creates child nodes within the parent_node based on the Firebase time stamp

func doTimestamp() {
    let timestampRef = self.ref.child("timestamp")
    timestampRef.setValue(kFirebaseServerValueTimestamp)
}

Here's the rundown.

In the attachObserver function, we attach an observer to the timestamp node - that node may or may not exist but if it doesn't it will be created - read on. The code in the closure is called any time an event occurs in the timestamp node.

When the doTimestamp function is called, it creates and writes a timestamp to the timestamp node, which then fires the observer we attached in attachObserver.

The code in the observe closure does the following:

Make sure the snapshot contains something, and if it does, increment a counter (more on that in a bit). If the counter is greater than 1 get the timestamp as an integer from the snapshot. Then, create it's negative and write it back out to Firebase as a child of parent_node.

How this would apply would be anytime you want to timestamp a child node with a Firebase generated timestamp but negative value for reverse loading/sorting - which speaks to the OP question.

The gotcha here is that when this happens

    timestampRef.setValue(kFirebaseServerValueTimestamp)

It actually writes twice to the node, which would cause the code in the closer to be called twice.

Maybe a Firebaser can explain that, but we need to ignore the first event and capture the second, which is the actual timestamp.

So the first event will cause the observer closer to fire, making count = 1, which will be ignored due to the if statement.

Then the second event fires, which contains the actual timestamp, and that's what we use to make negative and write out to Firebase.

Hope this helps the OP and the commenters.

like image 198
Jay Avatar answered Nov 13 '22 22:11

Jay


Regardless whether it's for Swift or not, another conceptual solution is to rely on Firebase's server time offset value.

It's not as precise as firebase.database.ServerValue.TIMESTAMP, but the difference is usually within milliseconds. The advantage is that it lets you create a negative timestamp on the client without having to update your Firebase node twice.

You grab the server time offset value when you need it from Firebase, generate the negative timestamp on the client, and then save your object in Firebase once.

See: https://groups.google.com/forum/#!topic/firebase-talk/EXMbZmyGWgE https://firebase.google.com/docs/database/ios/offline-capabilities#clock-skew (for iOS). https://firebase.google.com/docs/database/web/offline-capabilities#clock-skew (for web).

var offsetRef = firebase.database().ref(".info/serverTimeOffset");
offsetRef.on("value", function(snap) {
  var offset = snap.val();
  var negativeTimestamp = (new Date().getTime() + offset) * -1;
});
like image 44
Vitali Kniazeu Avatar answered Nov 14 '22 00:11

Vitali Kniazeu