Firebase Chat user is typing indicator cell

Viewed 1866

I have a Firebase realtime database to allow users to send direct messages to each other, which looks like this:

chats
-- fromUserID
---- toUserID
---- newMessages: Bool
---- isTyping: Bool
---- messageID_1
------ messageTimeStamp
---- messageID_2
------ messageTimeStamp
---- messageID_3
------ messageTimeStamp

messages
-- messageID
---- messageFromID
---- messageToID
---- messageText
---- messageTimeStamp
---- messageRead

As you can see, I have a key stored in the chats for each chat which is a boolean to indicate whether a user is typing or not. I change this in textFieldDidChange to true (if the textView.text != nil and if isSending = false, just to limit the requests). I set a timer to 10 seconds to automatically restore the "isTyping" value to false in case the user closes the app or whatever.

func textViewDidChange(_ textView: UITextView) {

    if(!isSending && textView.text != nil) {

        isSending = true

        if let messagePartner = messagePartner {

            Ref.child("chats").child(messagePartner).child(loggedInUserID).updateChildValues(["isTyping": true])

        }

    }

    if(isSending && textView.text == nil) {

        if let messagePartner = messagePartner {

            Ref.child("chats").child(messagePartner).child(loggedInUserID).updateChildValues(["isTyping": false])

        }
    }

}

func observeTyping() {

    if let messagePartner = messagePartner {

        typingObserver = Ref.child("chats").child(loggedInUserID).child(messagePartner).observe(.childChanged) { (snapTyping) in

            if(snapTyping.key == "isTyping") {

                let isTypingValue = snapTyping.value as? Bool

                if(isTypingValue == true) {

                    self.isTyping = true

                    if let messagePartnerFirstName = self.messagePartnerFirstName, let messagePartnerLastName = self.messagePartnerLastName {

                        self.navigationItem.title = messagePartnerFirstName + " " + messagePartnerLastName + " is typing..."

                    }

                } else {

                    self.isTyping = false
                    self.typingTimer?.invalidate()

                    if let messagePartnerFirstName = self.messagePartnerFirstName, let messagePartnerLastName = self.messagePartnerLastName {

                        self.navigationItem.title = messagePartnerFirstName + " " + messagePartnerLastName

                    }

                }

            }

        }

    }

}

Currently, I am changing the navigationItem.title to "username is typing..." when isTyping is true or just "username" when isTyping is false. This works like a charm.

This is pretty straight forward and easy to understand. However, I would like to change the behavior to show a cell of the user that is typing, with just "..." inside it, kind of like iOS Messenger and Facebook Messenger. There are, however, a couple of questions I have in terms of how to accomplish this.

First of all: I am returning the number of cells in my collectionView from messages.count. Should I append a message to messages.count when the user is typing?

Second, I would like to remove this cell once the user stopped typing or when the timer has fired (after 10 seconds). How should I do this at the right indexPath? Could I save the indexPath at which the cell is displayed and then remove it afterwards?

Third and last: I would like this cell to always be on the bottom of the chat. So when the other user enters a message, the ... indicator should still be on the bottom. Could I also accomplish that?

Any ideas or tips to point me in the right direction would be greatly appreciated! Thank you guys.

EDIT: Reflecting on my post, I am now considering another way: might it be a good idea to introduce another custom UICollectionViewCell, based on the chat cells but having only the ... inside it and changing the numberOfItemsInSection based on isTyping? Something like this:

if(isTyping) {
    return messages.count + 1
} else {
    return messages.count
}

I am just thinking out loud - please let me know what you guys think or if there is a better solution.

1 Answers

Eventually fixed it with a Supplementary View (Footer), which I change the height based on Bool isTyping (set frame to width & height 0 if not typing) and change the contentInset accordingly. Then when isTyping changes in the Fitebase observer I reloadData() to show or hide the Footer, works nice!

Related