title: Starting a firebase query trigger at a possibly changing start value to efficiently get data after that point in swift
body: <p><strong>Edit 2</strong></p>
  <p>This is my current setup. I am removing the observer when the side navigation view hides and restart it when it appears. This would be unnecessary if user defaults would return a fresh value in <code>queryStarting(atValue: defaults.double(forKey: lastReadMessageTimestampKey) + 0.000001)</code> like <code>timeIntervalSince1970</code> does in a similar query elsewhere: <code>queryStarting(atValue: NSDate().timeIntervalSince1970)</code>.</p>
  <pre><code>class SideNavigationController: UIViewController {
      
      ...
  
      var unreadMessageCountUpdateQueryHandle: DatabaseHandle?
  
      ...
      
      override func viewWillAppear(_ animated: Bool) {
          logger.info(&quot;SideNavigationControllers view will appear&quot;)
  
          updateUnreadMessagesCount()
  
          super.viewWillAppear(true)
          
          ...
      }
  
  
      override func viewWillDisappear(_ animated: Bool) {
          logger.info(&quot;SideNavigationControllers view will disapear&quot;)
          
          stopUpdatingUnreadMessagesCount()
          
          super.viewWillDisappear(animated)
          
          ...
      }
  
      func updateUnreadMessagesCount() {
          logger.info(&quot;Updating count of unread messages&quot;)
  
          let messageTimestampKey = DiscussionMessage.CodingKeys.messageTimestamp.stringValue
          let defaults = UserDefaults.standard
        
          unreadMessageCountUpdateQueryHandle = messagesReference.queryOrdered(byChild: messageTimestampKey).queryStarting(atValue: defaults.double(forKey: lastReadMessageTimestampKey) + 0.000001).observe(.value) { (snapshot) in
              if let value = snapshot.value as? [String: Any] {
                  print(&quot;Number of unread messages: \(value.count) Last Read TS \(UserDefaults.standard.double(forKey: lastReadMessageTimestampKey))&quot;)
                  self.menuItems[1].itemName = &quot;Discussions (\(value.count))&quot;
                  self.sideNavTableView.reloadData()
              } else {
                  self.menuItems[1].itemName = &quot;Discussions&quot;
                  self.sideNavTableView.reloadData()
              }
          }
      }
      
      func stopUpdatingUnreadMessagesCount() {
          guard let handle = unreadMessageCountUpdateQueryHandle else { return }
          messagesReference.removeObserver(withHandle: handle)
      }
  
  </code></pre>
  <p><strong>Edit:</strong></p>
  <p>Adding <code>.synchronize()</code> after saving the default value is not helping either.</p>
  <p>I have also tweaked the query a little bit to start with an offset from the last read message's timestamp so that it won't be included when I count unread messages.</p>
  <p>Now the query looks like this:</p>
  <pre><code>let discussionMessageTimestampKey = DiscussionMessage.CodingKeys.messageTimestamp.stringValue
  messagesReference.queryOrdered(byChild: discussionMessageTimestampKey)
  .queryStarting(atValue: UserDefaults.standard.double(forKey: lastReadMessageTimestampKey) + 0.000001)
  .observe(.value) { (snapshot) in
      if let value = snapshot.value as? [String: Any] {
          print(&quot;Number of unread messages: \(value.count) Last Read TS \(UserDefaults.standard.double(forKey: lastReadMessageTimestampKey))&quot;)
      }
  }
  </code></pre>
  <p>With this query initially the unread messages count is <code>0</code> and then it becomes 1, 2, 3, etc. as new messages come in. This is even after I open the chat view and the last read message timestamp gets updated inside the query. I know that the last read message timestamp is updating as the console output looks like this:</p>
  <pre><code>Number of unread messages: 1 Last Read TS TS_OF_SECOND_LAST_MESSAGE
  Number of unread messages: 2 Last Read TS TS_OF_SECOND_LAST_MESSAGE
  Number of unread messages: 3 Last Read TS TS_OF_SECOND_LAST_MESSAGE
  </code></pre>
  <p>And my database structure looks like this for people who are curious:</p>
  <p><a href="https://i.stack.imgur.com/86Lcw.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/86Lcw.png" alt="speech drill db structure 1" /></a>
  <a href="https://i.stack.imgur.com/wQZhT.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/wQZhT.png" alt="speech drill db structure 2" /></a></p>
  <p>Another way to do this might involve including 2 queries. Then the first query's execution could be stopped when the view is disappearing. But I don't know how to <a href="https://stackoverflow.com/questions/66319294/how-to-stop-the-execution-of-a-firebase-query-observeeventtypewith">stop a query execution</a> and could not find any documentation for it.</p>
  <pre><code>let messageTimestampKey = DiscussionMessage.CodingKeys.messageTimestamp.stringValue
  let defaults = UserDefaults.standard
  let query = messagesReference.observe(.childAdded) { (snapshot) in
      if snapshot.exists() {
          let startTimestamp = defaults.double(forKey: lastReadMessageTimestampKey)
          let offset: Double = 0.00000001
          messagesReference.queryOrdered(byChild: messageTimestampKey).queryStarting(atValue: startTimestamp + offset).observe(.value) { (snapshot) in
              if let value = snapshot.value as? [String: Any] {
                  print(&quot;Number of unread messages: \(value.count) Last Read TS \(UserDefaults.standard.double(forKey: lastReadMessageTimestampKey))&quot;)
              }
          }
      }
  }
  </code></pre>
  <hr>
  <p>I am creating a chat app and I want to count the number of unread messages. The value of the last read timestamp is saved at a location in my realtime database and each message has a timestamp attached to it. The last read message's timestamp is also stored in user defaults. So I tried using that value with <code>queryStarting(atValue:)</code>. But it is not working as expected. Even after the default's value gets updated, the query still uses an older value, and unread message count keeps going up with <code>.value</code> event. With <code>.childAdded</code> event, I keep getting a constant (always incorrect) count even when new messages are added.</p>
  <pre><code>let discussionMessageTimestampKey = DiscussionMessage.CodingKeys.messageTimestamp.stringValue
  messagesReference.queryOrdered(byChild: discussionMessageTimestampKey).queryStarting(atValue: UserDefaults.standard.double(forKey: lastReadMessageTimestampKey)).observe(.value) { (snapshot) in
      if let value = snapshot.value as? [String: Any] {
          print(&quot;Number of unread messages: \(value.count) Last Read TS \(UserDefaults.standard.double(forKey: lastReadMessageTimestampKey))&quot;)
      }
  }
  </code></pre>
  <p>I am using a <a href="https://stackoverflow.com/questions/39736714/in-firebase-ios-sdk-childadded-is-triggered-every-time-a-child-is-deleted-how/65957623#65957623">similar query</a> for firing <code>.childAdded</code> event only for newly added messages and not deleted messages. Here I have a strong feeling that <code>NSDate().timeIntervalSince1970</code> keeps returning a fresh value whenever a child is added or deleted. As even if 4 messages are added after the query was created and 1 of them was deleted, this event is not triggered. As it is designed to be triggered as explained by Frank <a href="https://stackoverflow.com/questions/39736714/in-firebase-ios-sdk-childadded-is-triggered-every-time-a-child-is-deleted-how/65957623#65957623">here</a>.</p>
  <pre><code>let discussionMessageTimestampKey = DiscussionMessage.CodingKeys.messageTimestamp.stringValue
  messagesReference.queryOrdered(byChild: discussionMessageTimestampKey).queryStarting(atValue: NSDate().timeIntervalSince1970).observe(.childAdded) { 
      (snapshot) in
      ...
  }
  </code></pre>
  <p>This brings me to my question. Since the user defaults query is not working, and the last read message timestamp is also stored in firebase. Can we maybe use that value as <code>queryStarting(atValue:)</code> instead (as I am hoping it will return a fresh value like <code>NSDate().timeIntervalSince1970</code> does). The problem here is that both the last read message timestamp and the number of messages can change. Hence if I create an observer on one, then it will lead to unnecessary data loading at another end. Could someone explain to me why the query works as expected with <code>NSDate().timeIntervalSince1970</code> but not with <code>UserDefaults</code>? And how I can fix this issue of counting number of unread messages optimally.</p>
  <p>(One method I thought of involved adding a KVO on user defaults. But I saw <a href="https://stackoverflow.com/questions/43963220/how-to-use-kvo-for-userdefaults-in-swift">that process</a> also has complications so I did not go down that route.)</p>