Ordering Firebase Posts Chronologically Swift

Ordering Firebase posts Chronologically Swift

The other answers all have merit but a more complete solution includes timestamping the post and denormalizing your data so it can be queried (assuming it would be queried at some point). In Firebase, flatter is better.

posts
post_0
title: "Posts And Posting"
msg: "I think there should be more posts about posting"
by_uid: "uid_0"
timestamp: "20171030105500"
inv_timestamp: "-20171030105500"
uid_time: "uid_0_ 20171030105500"
uid_inv_time: "uid_0_-20171030105500"

comments:
comment_0
for_post: "post_0"
text: "Yeah, posts about posting are informative"
by_uid: "uid_1"
timestamp: "20171030105700"
inv_timestamp: "-20171030105700"
uid_time: "uid_1_20171030105700"
uid_inv_time: "uid_1_-20171030105700"
comment_1
for_post: "post_0"
text: "Noooo mooooore posts, please"
by_uid: "uid_2"
timestamp: "20171030110300"
inv_timestamp: "-20171030110300"
uid_time: "uid_2_20171030110300"
uid_inv_time: "uid_2_-20171030110300"

With this structure we can

get posts and their comments and order them ascending or descending
query for all posts within the last week
all comments or posts made by a user
all comments or posts made by a user within a date range (tricky, huh)

I threw a couple of other key: value pairs in there to round it out a bit: compound values, query-ing ascending and descending, timestamp.

How do you properly order data from Firebase chronologically

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

Swift/Firebase - Sort posts in tableview by date

The question is little bit unclear but if you want to populate a tableView you will need to get the data from Firebase, populate an array (the dataSource), and reload the tableView.

First off we need to set up an .ChildAdded observer to load the data into the messagesArray, which is used as the tableView datasource

class AppDelegate: NSObject, NSApplicationDelegate {

var messagesArray = [[String:String]]() //an array of dicts, tableView datasource
var initialLoad = true

and the code to load the data initially and then watch for added messages

messagesRef.observeEventType(.ChildAdded, withBlock: { snapshot in

let dict = [String: String]()
dict["date"] = snapshot.value("date")
dict["msg"] = snapshot.value("message")
dict["sender"] = snapshot.value("sender")

self.messagesArray.append(dict)

if ( self.initialLoad == false ) { //upon first load, don't reload the tableView until all children are loaded
self.itemsTableView.reloadData()
}
})

then - and this is the cool part...

    //this .Value event will fire AFTER the child added events to reload the tableView
// the first time and to set subsequent childAdded events to load after each child
// is added in the future
messagesRef.observeSingleEventOfType(.Value, withBlock: { snapshot in

print("inital data loaded so reload tableView! \(snapshot.childrenCount)")
self.messagesTableView.reloadData()
self.initialLoad = false
})

With the above code, the order of the data is by their key - and if those keys were generated by Firebase, they will be sequential in the order the messages were created.

Your tableView is then populated from the messagesArray, and you can pick off the date, message and sender to put into the tableView columns or however you want the populate your cellView.

Your other requirement was to have them ordered by Date, descending. That's a super great question and had has answer here - it was a two part question but you'll see the thought process.

In Firebase, how can I query the most recent 10 child nodes?

you will also want to leverage Firebases query capabilities to get the specific data you want instead of the general data from above...

messagesRef.queryOrderedByChild("date").observeEventType(.ChildAdded, withBlock: {
snapshot in
//create a dict from the snapshot and add to tableview
})

How to sort data in Firebase?

There's a lot of code in the question and sometimes, simpler is better. So let's take a Post class, load the posts, get the associated user name and store it in an array. Then when complete, sort and print the posts in reverse chronological order.

A class to hold the post data and the user name

class PostClass {
var post = ""
var timestamp: Int! //using an int for simplicity in this answer
var user_name = ""

init(aPost: String, aUserName: String, aTimestamp: Int) {
self.post = aPost
self.user_name = aUserName
self.timestamp = aTimestamp
}
}

Note that if we want to have have both post data and user data we could do this

class PostUserClass {
var post: PostClass()
var user: UserClass()
}

but we're keeping it simple for this answer.

Then an array to store the posts

var postArray = [PostClass]()

and finally the code to load in all of the posts, get the associated user name (or user object in a full example).

let postsRef = self.ref.child("posts")
let usersRef = self.ref.child("users")
postsRef.observeSingleEvent(of: .value, with: { snapshot in
let lastSnapIndex = snapshot.childrenCount
var index = 0
for child in snapshot.children {
let childSnap = child as! DataSnapshot
let uid = childSnap.childSnapshot(forPath: "uid").value as! String
let post = childSnap.childSnapshot(forPath: "post").value as! String
let timestamp = childSnap.childSnapshot(forPath: "timestamp").value as! Int
let thisUserRef = usersRef.child(uid)

thisUserRef.observeSingleEvent(of: .value, with: { userSnap in
index += 1
//for simplicity, I am grabbing only the user name from the user
// data. You could just as easily create a user object and
// populate it with user data and store that in PostClass
// that would tie a user to a post as in the PostUserClass shown above
let userName = userSnap.childSnapshot(forPath: "Name").value as! String
let aPost = PostClass(aPost: post, aUserName: userName, aTimestamp: timestamp)
self.postArray.append(aPost) //or use self.postUserArray to store
// PostUserClass objects in an array.
if index == lastSnapIndex {
self.sortArrayAndDisplay() //or reload your tableView
}
})
}
})

and then the little function to sort and print to console

func sortArrayAndDisplay() {
self.postArray.sort(by: {$0.timestamp > $1.timestamp})

for post in postArray {
print(post.user_name, post.post, post.timestamp)
}
}

Note that Firebase is asynchronous so before sorting/printing we need to know we are done loading in all of the data. This is handled via the lastSnapIndex and index. The index is only incremented once each user is loaded and when all of the posts and users have been loaded we then sort and print as the data is complete.

This example avoids messy callbacks and completion handlers which may be contributing to the issue in the question - this piece of code is suspect and probably should be avoided due to the asynchronous nature of Firebase; the sort function is going to be called well before all of the users are loaded.

UserApi.shared.observeUserToPost(uid: userUid) { (user) in
self.postUser.append(user)
}
self.postUser.sort(by: {$0.postDate! > $1.postDate!})

*please add error checking.

Firebase query sort order in swift?

When Firebase loads the data into your tableView data source array, call this:

yourDataArray.sortInPlace({$0.date > $1.date})

Swift 3 Version:

yourDataArray.sort({$0.date > $1.date})

Swift 4 Version:

yourDataArray.sort(by: {$0.date > $1.date})

How to order fetched images from Firebase, from most recent to oldest?

So far I didnt see something like that function in exists in firebase, in my case after getting all the "post" data (it is arranged from oldest to new) I store the "data" returned from the real time database query in a object and using reverse() function in javascript I successfully sort the post from new old. Hope this helps

swift showing posts from all firebase users

You will need a different parent node called Books :-

Right now your JSON is something like this :-

posts : {
userID1 : {
BOOK_11_ID : {.....}
};

userID2 : {
BOOK_21_ID : {.....},
BOOK_22_ID : {.....},
BOOK_23_ID : {.....},
BOOK_24_ID : {.....},
}
}

You gotta modify your database JSON structure to :-

 posts : {

Users_books :{
userID1 : {
BOOK_11 : true // Value 'true' means that this user
// has subscribed or bought this book or whatever
};

userID2 : {
BOOK_21 : true,
BOOK_22 : true,
BOOK_23 : true,
BOOK_24 : true,
}
},

Books:{
BOOK_11_ID : {/Book details/};
BOOK_21_ID : {/Book details/};
BOOK_22_ID : {/Book details/};
BOOK_23_ID : {/Book details/};
BOOK_24_ID : {/Book details/};
}

}

Modify your security rules for section 'Book' to make is visible to all the authenticated users.

{
"rules" :{

"Users_books" : {

".write" : "auth != null && auth.uid === $uid", // Will allow only the user to make any change within its node and no-one else
".read": "auth != null && auth.uid === $uid"

},
"Books" : {
".write" : "auth != null", // Will allow all the users that are authenticated to your app to access every books detail.
".read" : "auth != null"
}
}
}

Now what you gotta do is :-

1) If its the user that is creating the book ; Then store the book details in the parent node 'Books' under the books uid and set the value of book_ID to true in the users node.

2) If the database is all backend i.e you input it; Whenever the user subscribe's or buys a books just fetch the uid of that book and set it to true in the section of that user.

PS : Always fan out your database ; its much easier to search from it .Although i am pretty sure you are gonna wanna store some personal details of the user in the near future so for that just make another node . Name it 'Users', security rules will be same as 'Users_books' and under 'Users' parent node append details of every which user with their uid as their key.



Related Topics



Leave a reply



Submit