1

Basically I am trying to make a twitter-like app, showing status updates of the people you follow, ordered by date, just like twitter. How can I do that? I have the code for displaying the posts in a table view, but the ordering is all messed up and looks quite random. I have a path called posts, with an autoID path, containing message, date and sender. How is this possible, without just showing the date in order from new > old, but also show the message and sender as well?

Thanks in advance.

root
  posts
    autoID
      sender
      message
      timestamp
 users
    UID
     username
Jay
  • 34,438
  • 18
  • 52
  • 81
askaale
  • 1,199
  • 1
  • 23
  • 50
  • please show come code, we cannot see any issues in the code if there isn't any to look at – Scriptable May 08 '16 at 19:18
  • @Scriptable Here is the code I have right now. Basically I just want to display the post by date, but I don't know how. Here is the link to the pastebin code: http://pastebin.com/P56g0a3h And also, how would I display just posts for the people I follow, in let's say the feed? Thanks in advance. – askaale May 09 '16 at 11:27
  • A couple of things; please post a code snippet in your question and most importantly post your Firebase structure (as text please, no images. Firebase Dashboard->export button). Secondly you if all your a doing in populating a tableView with Firebase data, ordered by date, you have way to much code. Last thing is; don't circumvent Firebase with other synchronous calls; wait to refresh your tableView until your data is loaded; that async call for reloading the tableView data will get you into trouble. If you can post your Firebase data structure, we can whip up a great answer. – Jay May 09 '16 at 17:28
  • @Jay Will do, I just had some problems pasting the code due to the character limit, so I figured out that I could use Pastebin. Second; here is the data structure: root{ posts{ autoID{ sender message timestamp } } users{ UID{ username } } } – askaale May 09 '16 at 18:45
  • I updated your post with your Firebase structure (please check) and in general it's good to add a code *snippit* of the code that is not working. Links should be avoided as they are not searchable for other folks that are having the same code issue and also, links change, which breaks the entire question. See how to write a [Minimal, Complete and Verifiable](http://stackoverflow.com/help/mcve) example. – Jay May 09 '16 at 21:27

3 Answers3

3

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
})
Community
  • 1
  • 1
Jay
  • 34,438
  • 18
  • 52
  • 81
  • Wow, thank you so much! This really helped me! However, I seem to be unable to access the keys in the messagesArray? I try with the following code in the population of the tableView: cell.textLabel?.text = messagesArray[indexPath.row].keys as? String The error message is 'Out of range'. – askaale May 10 '16 at 12:23
  • @askaale As it's set up, I am using an array of dictionaries, and the key is not included with each dictionary; only date, msg and sender (I omitted the key). You can store whatever is best for your code in the dictionary. You can add the key as a key:value pair along with the rest of the items in each dictionary. Then get the dict at index X, and the key will be keyValue = dict["key"]. There's a bunch of ways to handle store that data. – Jay May 10 '16 at 17:10
  • I have a rather strict knowledge about dictionaries, as I am struggling to see how I can store all this data in just one dictionary, and then display it from a table View. Would it be possible to create three separate arrays? – askaale May 10 '16 at 17:43
  • @askaale Technically you could do that but it's not necessary. You are not storing all of the data in a dictionary. You are storing each firebase child node's data in a dictionary and then each dictionary is stored in an array. The rows (indexes) in the array map to the rows in the tableView. So when the tableView row (cell) is being populated, you know it's index, which you then use to read the dictionary from the array dict = myArray[index). Then from the dict, you can get the values to populate the text in the cell; msg = dict["msg"] and then cell.textField.StringValue = msg for example. – Jay May 10 '16 at 17:53
  • I am very sorry for constantly asking, but as it seems right now - I have a problem with the table view. It seems that I can't access the messagesArray from the table view, as I keep getting an out of range-error. And also, what's the difference between the messagesArray and the dict-array? Why do the messagesArray contain two '[]'? Because of the timestamp, I changed both the dict and messagesArray to String:Any, doe to the fact that the timestamp is of type Bool. And also, would you please provide me with a straight example of just listing the 'messages' from the table view? From the cell – askaale May 10 '16 at 18:09
  • Again, thanks in advance. I am really sorry if I am disturbing you in any way, but this problem is really annoying me - as it seems so simple, yet i can't figure it out! haha! – askaale May 10 '16 at 18:09
  • Edit: Timestamp is of type Double, not bool. My bad. – askaale May 10 '16 at 18:41
  • See [Array of Dictionaries](http://stackoverflow.com/questions/24112627/initialising-empty-arrays-of-dictionaries-in-swift). The out of range error is probably because you need to define your array at the class level, not within the function so it will maintain the values i.e. at the top of the file, right after the class statement, add the array definition. I updated my answer with where to put it. I stuck mine in the AppDelegate as it was just for testing, you would want to put your in whatever class file you are using. As far as populating a tableView - that should be a separate question. – Jay May 10 '16 at 19:11
  • Oh - and there is not dict-array. It's simply an array of dictionaries with each dictionary having Strings as the key:value pairs; var messagesArray = [[String:String]]() – Jay May 10 '16 at 19:12
  • I still seem to have problems with showing the records from messagesArray in the table view. My code is like this(the records shows properly in print(), so I know for a fact that the data exists): cell.textLabel?.text = messagesArray[indexPath.row]["message"]. What do you think might cause the 'out of range'-error? – askaale May 11 '16 at 09:19
  • I figured it out. I forgot to reload the table view, stupid of me. Again, I really appreciate your help and effort. This has indeed helped me a lot! Again, thanks! – askaale May 11 '16 at 09:39
  • Can I reverse this order at all?? – Jorge Zapata Mar 06 '20 at 20:08
  • @user11234693 reverse the sort order? As in sort descending? Sure! See the link in my answer as it covers a descending sort. Essentially store a negative time stamp along with the positive time stamp and you can get your data ascending or descending. – Jay Mar 06 '20 at 20:36
  • But for this to be usable, I will need an array where as queryOrderedByChild will still give a dictionary of [String: Any]. So how do I go from this to an ordered list? I am using dict.map{ $0.1 } right now. But I am not sure if the order will be preserved. – Parth Jan 14 '21 at 10:01
  • @ParthTamane While this answer is almost 5 years old, the concepts are still the same. You will get an array by using the .childAdded code in the second section of the answer `self.messagesArray.append(dict)`. On the other hand, `queryOrderedByChild` does not give a Dictionary at all, it returns a DataSnapshot ordered by whatever child you specified. So, each node in the snapshot becomes a dictionary (via my code), but all of those are stored in an array correctly ordered. If you need additional help - open a question, post your code and we'll take a look! – Jay Jan 14 '21 at 20:00
0

You are currently getting all posts from Firebase, which would likely be ordered by the order they were added in, you can specify the order you want the results in like so,

firebase.queryOrderedByChild("posts/publishedDate").observeEventType(FEventType.Value, withBlock: { (snapshot:FDataSnapshot!) in
   self.firebaseUpdate(snapshot)
})

I converted that directly when writing from the objective-c example firebase provide, So it may need some tweaks.

You could also sort the results locally once you receive them, but you would likely need listOfMessages to be an array of dictionaries, classes or structs that contain the sort field, then you can use a predicate or filter to sort the array.

Sorting an array using filter

Firebase Retreiving data

Community
  • 1
  • 1
Scriptable
  • 19,402
  • 5
  • 56
  • 72
  • How exactly would I proceed if I have each 'sub path' to the posts-path (the post itself) generating a childbyAutoID for each post? Bu thank you very much, I did not know of this! This would probably be my last question. – askaale May 09 '16 at 17:13
  • sorry, I dont understand what you mean? in my example above there are multiple posts and each post has a publishedDate, so by specifying posts/publishedDate in the request, you can sort by a child value. What does your data look like in firebase? – Scriptable May 10 '16 at 08:50
  • @Scriptable This is not the answer. Look at the structure of the data. He doesnt have a path posts/publishedDate(or timestamp), but posts/autoID/timestamp. – Boomerange Sep 19 '17 at 10:16
0

For Swift 3:

Change . queryOrderedByChild('timestamp') for .queryOrdered(byChild: "timestamp")

Boomerange
  • 616
  • 1
  • 10
  • 18