0

I am trying to query firebase database in order to load more messages in a JSQMessagesViewController. When viewDidLoad is initialized, I make a query using childAdded and get the most recent 5 messages.

Thereafter, in didTapLoadEarlierMessagesButton I call prepareToObserveOnce method. Its purpose is to observe a single event of type value and get 5 more messages which are older than the ones that were downloaded earlier.

The problem is that when prepareToObserveOnce is called, the messages are inserted at the end of the array, rather than at the beginning.
When print(" self.arrayOfItems is \( self.arrayOfItems)") is called in observeChildAddedMessages it prints ["11", "12", "13", "14", "15"] , but when I call observeOnce(messageId: String), it prints ["11", "12", "13", "14", "15", "6", "7", "8", "9", "10"]. Somewhere my logic went south.

-messages
  -LKQdzoSTGqjSWu78t7R
     messageUID:"-LKQdzoSTGqjSWu78t7R"
     senderUID: "UfGFsZ57jEdXyrtRzEZ1i3wOBZJ2"
     type: "text"


-chats
  -LKQduebP-TEGM95kE5N
      chatUID: "-LKQduebP-TEGM95kE5N"
      lastMessage: "15"
  -messageIds
      -LKQeAJFMi5fvrdv4Xl6: "-LKQdzoSTGqjSWu78t7R"

My class

 class ChatVC: JSQMessagesViewController {

       var oldestKey: String! //keeps track of the oldest key of the oldest message download
       var firstMessagesLoaded: Bool = false //enables us to assign the oldest key of the message downloaded once all messages have been downloaded in observeChildAddedMessages method
       var arrayOfItems = String() // for test purposes

       var chatSelected: Chat!  //chat received from MessagesViewController
       var currentUser: UserModel!
       var messages = [Message]()
       var jsqMessages = [JSQMessage]()


         override func viewDidLoad() {
           super.viewDidLoad()

          //when view first loads, this method is called and will download the most recent 5 messages sent in chat
           self.observeChildAddedMessages()
       }


        override func collectionView(_ collectionView: JSQMessagesCollectionView!, header headerView: JSQMessagesLoadEarlierHeaderView!, didTapLoadEarlierMessagesButton sender: UIButton!) {
        //when the headerView is touched, this method should download 5 more messages earlier than oldestKey

             self.prepareToObserveOnce()
         }

    }//end of class


    extension ChatVC {

     //read the messages from the chat (if any) when viewDidLoad is initialized
     func observeChildAddedMessages() {

        //  ~/chats/childByAutoId/messageIds
        let chatMessageIdsRef = chatSelected.chatRef.child("messageIds")

        chatMessageIdsRef.queryOrderedByKey().queryLimited(toLast: 5).observe(.childAdded, with: { (snapshot) in
            let messageId = snapshot.value as! String
            let messageKey = snapshot.key

            if self.firstMessagesLoaded == false {
                self.oldestKey = messageKey
                self.firstMessagesLoaded = true
            }

            // ~/messages/messageId
            let messagesRef = DDatabaseRReference.messages.reference()
            messagesRef.child(messageId).observeSingleEvent(of: .value, with: { (snapshot) in
                let message = Message(dictionary: snapshot.value as! [String : Any])

                if !self.messages.contains(message) {
                    let jsqMessage = JSQMessage(senderId: message.senderUID, displayName: message.senderDisplayName, text: message.text)
                    self.messages.append(message)
                    self.jsqMessages.append(jsqMessage!)
                     print(" self.arrayOfItems is \( self.arrayOfItems)")
                    self.finishReceivingMessage()
                 }
             })//end of messagesRef.child(messageId).observeSingleEvent
         })//end of chatMessageIdsRef.queryOrderedByKey()
      }//end of observeChildAddedMessages
    }//end extension


    extension ChatVC {

       func prepareToObserveOnce() {

         //  ~/chats/childByAutoId/messageIds
         let chatMessageIdsRef = chatSelected.chatRef.child("messageIds")

         if oldestKey == nil {

            chatMessageIdsRef.queryOrderedByKey().queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { snapshot in

                guard snapshot.exists() else {return}
                let firstItem =  snapshot.children.allObjects.first as! DataSnapshot
                self.oldestKey = firstItem.key

                for item in snapshot.children.allObjects as! [DataSnapshot] {
                    let messageId = item.value as! String
                    self.observeOnce(messageId: messageId)
                }
            })

        } else {
             //get 5 more elements relative to oldestKey, meaning that we get the oldestKey inclusive. - .queryEnding(atValue: oldestKey)
            //These are earlier messages than currentKey
            chatMessageIdsRef.queryOrderedByKey().queryEnding(atValue: oldestKey).queryLimited(toLast: 6).observeSingleEvent(of: .value, with: { snapshot in

                guard snapshot.exists() else {return}
                let firstItem = snapshot.children.allObjects.first as! DataSnapshot

                for item in snapshot.children.allObjects as! [DataSnapshot] {
                    let messageId = item.value as! String
                    //avoid getting same message twice by checking the key of the message downloaded != currentKey
                    if firstItem.key != self.oldestKey {
                        self.observeOnce(messageId: messageId)
                    }
                 }
             })
         }  
      }//end prepareToObserveOnce
    }//end extension


    extension ChatVC {

      func observeOnce(messageId: String) {

          // ~/messages/messageId
         let messagesRef = DDatabaseRReference.messages.reference()
         messagesRef.child(messageId).observeSingleEvent(of: .value, with: { (snapshot) in
            let message = Message(dictionary: snapshot.value as! [String : Any])

            if !self.messages.contains(message) {

                let jsqMessage = JSQMessage(senderId: message.senderUID, displayName: message.senderDisplayName, text: message.text)

                self.messages.append(message)
                self.jsqMessages.append(jsqMessage!)
                 print(" self.arrayOfItems is \( self.arrayOfItems)")
            }
         })
       }//end of observeOnce
    }//end of extension
bibscy
  • 2,598
  • 4
  • 34
  • 82

1 Answers1

0

There were 2 problems.
1. was that I was appending the new messages to the array rather than inserting them at index 0.

  1. The messages downloaded from firebase arrive in ascending order, thus I had to download all 5 messageID's , assign them to a temporary array named arrayOfMessageID and thereafter loop through it and self.observeOnce(messageId: messageID) for each messageID.
    Prior to this approach, after each downloaded messageID, I was calling self.observeOnce(messageId: messageID) and in this way I would have had no way of ordering the messages prior to assigning them to messages or jsqMessages. Or there could have been, but not a neat one.

My class

  `class ChatVC: JSQMessagesViewController {`

       var oldestKey: String! //keeps track of the oldest key of the oldest message download
       var firstMessagesLoaded: Bool = false //enables us to assign the oldest key of the message downloaded once all messages have been downloaded in observeChildAddedMessages method
       var arrayOfItems = String() // for test purposes

       var chatSelected: Chat!  //chat received from MessagesViewController
       var currentUser: UserModel!
       var messages = [Message]()
       var jsqMessages = [JSQMessage]()


         override func viewDidLoad() {
           super.viewDidLoad()

          //when view first loads, this method is called and will download the most recent 5 messages sent in chat
           self.observeChildAddedMessages()
       }


        override func collectionView(_ collectionView: JSQMessagesCollectionView!, header headerView: JSQMessagesLoadEarlierHeaderView!, didTapLoadEarlierMessagesButton sender: UIButton!) {
        //when the headerView is touched, this method should download 5 more messages earlier than oldestKey

             self.prepareToObserveOnce()
         }

    }//end of class


    extension ChatVC {

     //read the messages from the chat (if any) when viewDidLoad is initialized
     func observeChildAddedMessages() {

        //  ~/chats/childByAutoId/messageIds
        let chatMessageIdsRef = chatSelected.chatRef.child("messageIds")

        chatMessageIdsRef.queryOrderedByKey().queryLimited(toLast: 5).observe(.childAdded, with: { (snapshot) in
            let messageId = snapshot.value as! String
            let messageKey = snapshot.key

            if self.firstMessagesLoaded == false {
                self.oldestKey = messageKey
                self.firstMessagesLoaded = true
            }

            // ~/messages/messageId
            let messagesRef = DDatabaseRReference.messages.reference()
            messagesRef.child(messageId).observeSingleEvent(of: .value, with: { (snapshot) in
                let message = Message(dictionary: snapshot.value as! [String : Any])

                if !self.messages.contains(message) {
                    let jsqMessage = JSQMessage(senderId: message.senderUID, displayName: message.senderDisplayName, text: message.text)
        //we can safely append messages here in .childAdded, because this method is called when viewDidLoad is initialized, so there are no other items in the array.
                     self.messages.append(message)
                     self.jsqMessages.append(jsqMessage!)
                     print(" self.arrayOfItems is \( self.arrayOfItems)")
                    self.finishReceivingMessage()
                 }
             })//end of messagesRef.child(messageId).observeSingleEvent
         })//end of chatMessageIdsRef.queryOrderedByKey()
      }//end of observeChildAddedMessages
    }//end extension


    extension ChatVC {

       func prepareToObserveOnce() {

         //  ~/chats/childByAutoId/messageIds
         let chatMessageIdsRef = chatSelected.chatRef.child("messageIds")

         if oldestKey == nil {

            chatMessageIdsRef.queryOrderedByKey().queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { snapshot in

                guard snapshot.exists() else {return}
                let firstItem =  snapshot.children.allObjects.first as! DataSnapshot
                self.oldestKey = firstItem.key

                for item in snapshot.children.allObjects as! [DataSnapshot] {
                    let messageId = item.value as! String
                    self.observeOnce(messageId: messageId)
                }
            })

        } else {
             //get 5 more elements relative to oldestKey, meaning that we get the oldestKey inclusive. - .queryEnding(atValue: oldestKey)
            //These are earlier messages than currentKey
            chatMessageIdsRef.queryOrderedByKey().queryEnding(atValue: oldestKey).queryLimited(toLast: 6).observeSingleEvent(of: .value, with: { snapshot in

                guard snapshot.exists() else {return}
                let firstItem = snapshot.children.allObjects.first as! DataSnapshot

              //create an array of messageID in order to reverse the messages as they arrive in ascending order and we can nor append neither insert at a specific index because they would appear like [10,9,8,7]
            var arrayOfMessageID = [String]()

                for item in snapshot.children.allObjects as! [DataSnapshot] {
                    let messageId = item.value as! String
                    //avoid getting same message twice by checking the key of the message downloaded != currentKey
                    if firstItem.key != self.oldestKey {
                       arrayOfMessageID.append(messageId)
                    }
                 }
                     arrayOfMessageID.reverse()
                      for messageID in arrayOfMessageID {
                         self.observeOnce(messageId: messageID)
                     }
          //after downloading the messages is complete, set oldestKey as the firstItem downloaded. Remember, the download is done from low to high based on key timeStamp based, which means the firstItem downloaded is the oldest
                      self.oldestKey = firstItem.key
             })
         }  
      }//end prepareToObserveOnce
    }//end extension


    extension ChatVC {

      func observeOnce(messageId: String) {

          // ~/messages/messageId
         let messagesRef = DDatabaseRReference.messages.reference()
         messagesRef.child(messageId).observeSingleEvent(of: .value, with: { (snapshot) in
            let message = Message(dictionary: snapshot.value as! [String : Any])

            if !self.messages.contains(message) {

                let jsqMessage = JSQMessage(senderId: message.senderUID, displayName: message.senderDisplayName, text: message.text)

                    self.messages.insert(message, at: 0)
                    self.jsqMessages.insert(jsqMessage!, at: 0)

               //DO NOT append! Wrong way
                self.messages.append(message)
               self.jsqMessages.append(jsqMessage!)

                 print(" self.arrayOfItems is \( self.arrayOfItems)")
            }
         })
       }//end of observeOnce
    }//end of extension
bibscy
  • 2,598
  • 4
  • 34
  • 82