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.