2

I try to load the posts I stored on firebase into my tableView. I used the .childAdded function to get the post in the order they have been posted (first to last). First it seemed to work, but now it doesn't work no more and I don't know why. So I added a timestamp to the posts and used queryOrderedByChild("timestamp"). Still the wrong order though! Here is my code:

posts.removeAll()
    let ref = FIRDatabase.database().reference()

    ref.child("Posts").queryOrderedByChild("timestamp").observeEventType(.ChildAdded, withBlock: { (snapshot:FIRDataSnapshot) in

        print(snapshot.value!["timestamp"] as! Int)
        if snapshot.value == nil {
            return
        }
        let post = Post()
        post.title = snapshot.value!["Title"] as? String
        post.postType = snapshot.value!["postType"] as? Int
        post.postDescription = snapshot.value!["Description"] as? String

        if post.postType == 2 {
            post.imageAURL = snapshot.value!["imageAURL"] as? String
            post.imageBURL = snapshot.value!["imageBURL"] as? String
        }else if post.postType == 3 {
            post.imageAURL = snapshot.value!["imageAURL"] as? String
            post.imageBURL = snapshot.value!["imageBURL"] as? String
            post.imageCURL = snapshot.value!["imageCURL"] as? String
        }


        let createdByID = snapshot.value!["createdBy"] as! String
        var username = String()
        let usernameRef = FIRDatabase.database().reference().child("users").child(createdByID)
        usernameRef.observeSingleEventOfType(.Value, withBlock: { (snapshot:FIRDataSnapshot) in

            username = snapshot.value!["username"] as! String
            post.createdBy = username
            self.posts.append(post)
            self.tableView.reloadData()
            }, withCancelBlock: nil)




            dispatch_async(dispatch_get_main_queue(), {

                self.tableView.reloadData()
            })


        }, withCancelBlock: nil)


}

the print of the timestamp value in the beginning of the query puts out:

1471008028
1471007899
1471007928
1471007979

As you can see, the first Int is the highest, the next three are ordered correctly in ascending order, but why is the highest first and not last? I don't know if it has anything to do with it, but the code is inside a function that is called inside viewDidLoad.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
beginner_T
  • 417
  • 1
  • 6
  • 21
  • I posted the posted from different user accounts. I just noticed, that the posts of the user that is currently logged in are always the ones that are loaded first, but why? Means that if I log in as a different user and start the simulator again, the order changed, but the .ChildAdded function shouldn't care about what user is logged in, because it just loads all posts, no matter who posted them? – beginner_T Aug 12 '16 at 13:42
  • This has been asked and answered [before](http://stackoverflow.com/a/15723418/6680611). – cartant Aug 12 '16 at 15:01
  • I guess that answer is kinda out of date, correct me if I'm wrong. Also there is no sample in swift, so it would be great if someone had some solution in swift or can edit my code in order to work the way it should – beginner_T Aug 12 '16 at 15:44
  • also I don't get why the ordering by the timestamp doesn't seem to work – beginner_T Aug 12 '16 at 16:06
  • Can you please update your question with a snippet of your Firebase structure as text please, no images. – Jay Aug 12 '16 at 17:41

1 Answers1

1

There is an existing answer that explains why Firebase JavaScript child_added events occur out-of-order. It is still applicable and is the reason why your snapshots are being delivered in an unexpected order.

I know this may seem strange, but this is actually the intended behavior.

In order to guarantee that local events can fire immediately without communicating with the server first, Firebase makes no guarantees that child_added events will always be called in sort order.

To arrange the received snapshots into the correct order, you need the previous sibling key that accompanies the snapshot (it's referred to as the prevChildName in the referenced answer). To obtain the previous sibling key, you need to use observeEventType:andPreviousSiblingKeyWithBlock:.

The Firebase documentation for observeEventType:andPreviousSiblingKeyWithBlock: does not make it clear how the previous sibling key ought to be used to arrange the received snapshots.

For the purposes of an example, to store and order the snapshots in an array, you would need to do the following for each received a snapshot:

  • if the received snapshot has a null previous sibling key, add it to the head of the array;
  • otherwise, find the snapshot in the array that has a key equal to the previous sibling key (that accompanied the recieved snapshot) and insert the received snapshot into the array after the found snapshot;
  • if there is no snapshot with the previous sibling key, add the received snapshot to the tail of the array.
Community
  • 1
  • 1
cartant
  • 57,105
  • 17
  • 163
  • 197
  • At the moment I don't have time to try that solution, but I really appreciate your answer and I'm marking it as right answer, since I think that should work! Thanks a lot! Still I don't get why the orderedByChild with the timestamp doesn't work?! – beginner_T Aug 15 '16 at 12:11
  • It's a pain, I know. It's just the way it is - there are no guarantees. If you look at Firebase's [AngularFire2](https://github.com/angular/angularfire2/blob/2.0.0-beta.2/src/utils/firebase_list_factory.ts#L150-L165) source, you'll see they account for it there with a `reduce`-based mechanism. – cartant Aug 15 '16 at 12:18
  • So do you know why using a timestamp and orderedByChild does not work? As far as I read the docs, it should be ordering the snapshot in ascending order right? Why doesn't it work? – beginner_T Aug 21 '16 at 00:12
  • I just found out something. My app used to start at the profile page of the logged in user, which loads all of the users posts. If I comment out the loading of the users posts and switch to this view (the view that calls the code I posted), everything works perfect. Only if I load the users posts before, the order is wrong and only the posts by the user himself are placed wrong. So does it have anything to do with the fact that I already kind of loaded these posts? – beginner_T Aug 21 '16 at 01:09