0

I have a ref and I only want to listen when new items are added (it's in viewWillAppear below). When a new item is added newItemsButton appears. If nothing new is added to what already exists inside the db then the button should't appear.

The problem is as soon as the observer starts to observe, the newItemButton appears even though nothing new was added. What am I doing wrong?

db:

-posts
   -postId_1
   -postId_2
   -postId_3

code:

var startKey: String?
let postsRef = Database.database().reference().child("posts")
let postsObserverRef = Database.database().reference().child("posts")

override func viewDidLoad() {
    super.viewDidLoad()

    paginate()
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    listenForNewItems()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    postsObserverRef.removeAllObservers()
}

func listenForNewItems() {

    postsObserverRef.observe(.childAdded) { (snapshot) in

        let postId = snapshot.key
        let isContained = self.dataSource.contains { $0.postId == postId }

        if !isContained {

            // show newItemsButton
        }
    }
}

func paginate() {

    if startKey == nil {

        postsRef.queryOrderedByKey().queryLimited(toLast: 20)
            .observeSingleEvent(of: .value, with: { (snapshot) in
            // fill datasource
        })

    } else {

        postsRef.queryOrderedByKey().queryEnding(atValue: startKey).queryLimited(toLast: 21)
            .observeSingleEvent(of: .value, with: { (snapshot) in
            // fill datasource
        })
    }
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • All you need to do is leverage that `.childAdded` initially fires once for each child node and then for newly added child nodes after that. Use the initial events to populate your array initially! In other words - you don't need the .observeSingleEvent as well as .childAdded - just use .child added. – Jay Jun 17 '20 at 18:05
  • How do I get the last child? Do I have to do what I did in the listenForNewItems (in my answer) and create a local array and a timer to get the last item from the array? – Lance Samaria Jun 17 '20 at 18:08
  • Are you asking how to get the last child node? [.queryLimitedToLast](https://firebase.google.com/docs/database/ios/lists-of-data#filtering_data)? Or something else? I don't understand the rest of the question but anything related to a 'timer' for Firebase data, the answer would be no. It's not clear what the code in `listenForNewItems' is for but if you have a .childAdded observer on a node, the 'last item' will be the one just added (that fit the query constraints). – Jay Jun 17 '20 at 18:17
  • I'm having the same problem this person has https://stackoverflow.com/q/40497336/4833705 , I need to listen for new items, not for anything that already exists. Since .childAdded fires first the newItemsButton will always appear. First I want to get the last item from .childAdded,if it's in the dataSource array, the button won't appear. After that I only have to worry about newItems being added. – Lance Samaria Jun 17 '20 at 18:23
  • no need to respond to my previous comment, as I said below I didn't know that queryLimitedToLast can be combined with .childAdded. – Lance Samaria Jun 17 '20 at 18:28
  • So you're saying you never load in any items other than the one just added? I am not sure I understand that since that would mean it would load one item, and show it in the UI. Then when the user added another, the first one goes away and then the new ons is displayed. I don't know your use case is but typically a list of items is loaded and displayed in a tableView and then when new items are added, they are append to that list. Think of a To Do list app where when the user starts the app, the to-todo's a loaded and displayed and then when new ones are added, they add to the end of that list. – Jay Jun 17 '20 at 18:31
  • *I didn't know that queryLimitedToLast can be combined with .childAdded.* but why would you want to do that? There's no need based on your question. – Jay Jun 17 '20 at 18:32
  • The way I have the app is the most recent items loaded first and as the user scrolls the older ones show next. So when viewDidLoad first appears anything posted today will show first, yesterday next, etc etc. Today is Wed (which would show first) if the user scrolls down and is viewing what was posted on Monday, if new items are automatically added they will automatically go to the top without the user knowing. If they go to the bottom they will be out of order. That’s why a button appears. They press it and go back up to the top and start over. Similar to Instagram – Lance Samaria Jun 17 '20 at 18:36
  • 1
    Ok. Got it. The solution is very simple. Add a `asc_timestamp` and `des_timestamp` child nodes (ascending and descending). Store positive timestamps in the `asc_timestamp` child and and negative timestamps in the `des_timestamp`. So `asc_timestamps` would be 20200601, 2020602, 20200603 and -20200601, 20200602, -20200603 in the desc_timestamp. Then use a .childAdded query, ordered by `des_timestamp`. That will make your data load from most current to oldest and as additional 'newer' children are added they will fire the childAdded event so you can `array.insert(at: 0)` to put it first. – Jay Jun 17 '20 at 18:52
  • All the posts have a timestamp property like “postDate”. But doesn’t .childByAutoId() automatically sort by date? The postIds are generated using .chipdByAutoId() – Lance Samaria Jun 17 '20 at 18:55
  • And see my answer the this question about [getting list of data in order (Last data first)](https://stackoverflow.com/questions/58148894/firebase-getting-list-of-data-in-order-last-data-first/58149028#58149028) as it illustrates the structure mentioned above. – Jay Jun 17 '20 at 18:55
  • Thanks for the link and above response. That’s exactly what I need – Lance Samaria Jun 17 '20 at 18:56
  • `.childByAutoId()` has nothing to do with sorting or dates. It just assigns a lexicographic string as the key value of a node which provides some 'natural ordering' to the data but there are usually better options for determining a desired order. – Jay Jun 17 '20 at 18:58
  • Oh ok thanks. I thought on googles end dates where somehow tied to it. I’ll look at everything you added and see what I can figure out. Thanks and much appreciated. I’ll post an answer once I get it working correctly – Lance Samaria Jun 17 '20 at 18:59

0 Answers0