How do you properly order data from Firebase chronologically

11,258

Solution 1

Using only reverse() for your array is not enough way to encompass everything. There are different things you need to think about:

  • Limit while retrieving data, use append() and then reverse() to save time. You don't need to delete all array for each time.

  • Scroll trigger or willDisplay cell method loading

Let's start. You can create a child for your posts timestamp or date/time being global. To provide like Instagram seconds, weeks I advice you using UTC time. So I will call this: (timeUTC)

For sorting your all post, use since1970 timestamp. So I will call this (timestamp) and then also you can keep another node as (reversedTimestamp) adding - prefix to timestamp. So when you use queryOrdered to this node. You can handle latest 5 post using with yourQuery.queryLimited(toFirst: 5).

1.Get UTC date/time for timeUTC node in Swift 3:

        let date = Date()
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        formatter.timeZone = TimeZone(abbreviation: "UTC")
        let utcTimeZoneStr = formatter.string(from: date)

+0000 means it's universal time, look at http://time.is/tr/UTC

2.Get since1970 timestamp to sort posts in Swift 3:

let timestamp = (Date().timeIntervalSince1970 as NSString).doubleValue
let reversedTimestamp = -1.0 * timestamp

Now, you can save them on your Firebase posts like this.

"posts" : {
    "-KHLOy5mOSq0SeB7GBXv" : {
      "timestamp": "1475858019.2306"
      "timeUTC" : "2012-02-04 12:11:56 +0000"
    },
    "-KHLrapS0wbjqPP5ZSUY" : {
      "timestamp": "1475858010.1245"
      "timeUTC" : "2014-02-04 12:11:56 +0000"
    },

I will retrieve five by five post, so I'm doing queryLimited(toFirst: 5) in viewDidLoad:

let yourQuery = ...queryOrdered(byChild: "reverseTimestamp")
                   .queryEnding(atValue: "\(self.pageOnTimestamp)", childKey: "reverseTimestamp")

    yourQuery.observeSingleEvent(of: .value, with: { (snapshot) in

        if snapshot.value is NSNull {

            print("There is no post.")

        }
        else {

            yourQuery.queryLimited(toFirst: 5).observeSingleEvent(of: .value, with: { (snapshot) in

                self.posts.removeAll(keepingCapacity: true)

                for (i, snap) in snapshot.children.enumerated() {

                    if let postAllDict = snapshot.value as? [String: AnyObject] {
                        if let postDict = postAllDict[(snap as AnyObject).key as String] as? [String: AnyObject] {

                            let post = Post(key: (snap as AnyObject).key as String, postDict: postDict)
                            self.posts.append(post)
                        }
                    }
                }

                completion(true)
            })
        }
    })

If user reached latest post, you can handle it with willDisplay method like below, then you can call loadMore function.

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {

   if self.posts.count - 1 == indexPath.row {
      // call loadMore function.
   }
}

In loadMore() function you can handle latest post's timestamp, then start-end query as with that, so you can easily continue with next first 5 posts while appending before array.

For Swift 3 conversion as nice formatted, take a look here: Swift 3 - UTC to time ago label, thinking 12h / 24h device time changes

Solution 2

Based on @tobeiosdev answer i define my data structure like this:

"posts" : {
"-KcOa8GXBl08V-WXeX8P" : {
  "author" : "@giovanny.piñeros",
  "hashtags" : [ "Hello", "World" ],
  "text" : "Hola Mundo!!!!!",
  "timestamp" : 5.08180914309278E8,
  "title" : "Hello World",
  "userid" : "hmIWbmQhfgh93"
}

As you can see i've added a timestamp attribute, when i query my data with a child added event, i pass that data to a post object, then i append that object to an array of posts from which my table view will feed:

 self.posts.append(post)
 self.posts.sort(by: {$0.timestamp! > $1.timestamp!})
 self.tableViewFeed.reloadData()

With the sort method i've managed to order my data in the desire order of posts, from the newest to the oldest. I Hope this approach could help anyone :).

Share:
11,258

Related videos on Youtube

kerbelda
Author by

kerbelda

Updated on June 26, 2022

Comments

  • kerbelda
    kerbelda almost 2 years

    I'm trying to order my data from Firebase so the most recent post is at the top (like Instagram), but I just can't get it to work properly. Should I be using a server timestamp? Is there a "createdAt" field?

    func getPosts() {
        POST_REF.observeEventType(.Value, withBlock: { snapshot in
            guard let posts = snapshot.value as? [String : [String : String]] else {
                print("No Posts Found")
                return
            }
    
            Post.feed?.removeAll()
            for (postID, post) in posts {
                let newPost = Post.initWithPostID(postID, postDict: post)!
                Post.feed?.append(newPost)
            }
            Post.feed? = (Post.feed?.reverse())!
            self.tableView.reloadData()
            }, withCancelBlock: { error in
                print(error.localizedDescription)
        })
    }
    
  • kerbelda
    kerbelda about 8 years
    Thank you so much, this really helped and solved my issue. Also, I was looking into pagination, so that's awesome! Do you have to use single event for pagination? Because that won't automatically update the feed when new posts are submitted anymore.
  • iamburak
    iamburak about 8 years
    @kerbelda I think observing every changes in descending order is not true approach for my explanation. It can effect user experience if user read a cell that case can trigger scroll. If you observe and then add my tableView array, you can see It won't work in proper way. Instead, you can add a navigation item for refreshing (call viewDidLoad: again go top of the screen) or check self.tableView.contentOffset.y is equal to 0 status in scrollViewDidScroll() and then you can call viewDidLoad: to load last first 5 post automatically.
  • Link Openheim
    Link Openheim over 7 years
    @tobeiosdev would you have an email I could contact you at?