Reputation: 2484
I'm trying to code a chat app.
I would like to count the number of messages, which are not viewed (unseen).
My database looks like this:
user-messages
|
|_messages
|
|_UserID1
| |
| |_UserID2
| |
| |_ MessageID1
| |_ MessageID2
| |_ MessageID3
| |_ etc...
|
|_UserID2
| |
| |_UserID1
| |
| |_ MessageID1
| |_ MessageID2
| |_ MessageID3
| |_ etc...
And for the Messages object:
messages
|
|_messageID1
| |
| |_ notViewed: false / true
| |_ text: "Message text"
| |_ timestamp: 1522230692
| |_ etc...
|
|_messageID2
| |
| |_ notViewed: false / true
| |_ text: "Message text"
| |_ timestamp: 1522230692
| |_ etc...
I arrived to get all the message ID which are linked to a specific user:
var REF_MESSAGE = Database.database().reference().child("messages")
var REF_USER_MESSAGE = Database.database().reference().child("user-messages").child("messages")
func fetchUnviewedMessages(withId id: String, completion: @escaping (Int) -> Void ) {
REF_USER_MESSAGE.child(id).observe(.childAdded) { (snapshot) in
let userId = snapshot.key as String
self.REF_USER_MESSAGE.child(id).child(userId).observe(.childAdded, with: { (snapshot) in
let messageId = snapshot.key
// This part is where I have a problem
self.fetchMessageNotViewed(messageId, completion: { (nbMessageNotRead) in
completion(nbMessageNotRead)
})
})
}
}
But I don't know what I need to do with that list, to observe the number of unseen messages ...
I tried that, but without success:
func fetchMessageNotViewed(_ messageId: String, completion: @escaping (Int) -> Void) {
guard let currentUid = Api.User.CURRENT_USER?.uid else {
return
}
REF_MESSAGE.child(messageId).queryOrdered(byChild: "notViewed").queryEqual(toValue: true).observe(.value, with: { (snapshot) in
let count = Int(snapshot.childrenCount)
print(count)
}, withCancel: nil)
}
Upvotes: 1
Views: 3674
Reputation: 35658
We can leverage a compound value to handle this fairly easily.
Make a simple change to the Firebase structure messages node:
messages
|
|_messageID1
| |
| |_ notViewed: false / true
| |_ text: "Message text"
| |_ timestamp: 1522230692
| |_ forUid_status: "UserId1_unread" //<- Compound value
| |_ etc...
|
|_messageID2
| |
| |_ notViewed: false / true
| |_ text: "Message text"
| |_ timestamp: 1522230692
| |_ forUid_status: "UserId1_read" //<- Compound value
| |_ etc...
then a simple query will reveal a count of UserId1 unread messages
let messagesRef = self.ref.child("messages")
let uid = "UserId1"
let status = "unread"
let queryString = uid + "_" + status //results in a string UserId1_unread
let ref = messagesRef.queryOrdered(byChild: "forUid_status").queryEqual(toValue: queryString)
ref.observe(.value, with: { snapshot in
if snapshot.exists() {
let count = snapshot.childrenCount
print("unread messages: \(count)")
} else {
print("no unread messages")
}
})
running this code against the above Firebase results in
unread messages: 1
This may also help with the loading of unread messages as not only can you get their count but adding the observer to that node will also notify the app any time a new unread message is added for UserId1
Upvotes: 3
Reputation: 1822
As on request by @KevinB
You can use my logic to show the counter in message popup
Step:-1 WebService call/Firebase Fetch:
Fetch the array of message data in app.In my case, i was having an array of messages in response and store the count of it an userDefault. For E.g:-
UserDefaults.standard.set(arrMessageData.count, forKey: "messageCount")
Step:-2 Set Counter-Zero when message is read Or Appending the old value with new value
Case:1 Reading the Message
In my case i was having a separate popup for reading the message. when that pop-up was opened i was forcefully making the value of my userDefault value to 0
UserDefaults.standard.set(0, forKey: "messageCount")
Case:1 If message is un-read
In that case if pop-up is not opened so in that case there will be some value in userDefault. In this case First fetch the current count in userDefault and then make the WSCall and get the new count and Add both and then save in same key.
Note:- The idea of storing the counter in userDefault is used because it can be accessed anywhere in app and can directly be shown in any label or button.
Upvotes: 0