'Firebase chat adds message in other conversation when online
We have implemented a chat application both in iOS and Android which is 1:1 chat, and the structure is also well designed. Recently we figured out a bug which sends message to another user when they are online. Refer the eg
Suppose there are 3 People, User - A, User - B and User - C
User - B sends a message to User - A User - C sends a message to User - A
Now, User A is in conversation with User - B and User - C sends a message to User - A that message comes in User - A and User - B Chat....
First we thought it was a bug on the iOS side and later on we tested the same with Android and it behaves the same way.
Below is the Firebase Structure
So it is like
- Chat
- USER-A-ID-USER-B-ID
- RandomID
- Chat Data
- RandomID
- USER-A-ID-USER-B-ID
Below is the code for Reference
let childRef = Database.database().reference().child("chat").child(UserDefaults.standard.string(forKey:"UserId")!+"-"+UserDefaults.standard.string(forKey:"receiverUserId")!)
let childRef1 = Database.database().reference().child("chat").child(UserDefaults.standard.string(forKey:"receiverUserId")!+"-"+UserDefaults.standard.string(forKey:"UserId")!)
let key = childRef.childByAutoId().key
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy HH:mm:ss"
formatter.timeZone = TimeZone.init(abbreviation: "UTC")
let result = formatter.string(from: date)
let message = ["sender_id":UserDefaults.standard.string(forKey:"uid")!,"receiver_id":UserDefaults.standard.string(forKey:"receiverUID")!,"sender_name":UserDefaults.standard.string(forKey:"UserName")!,"text":text,"timestamp":result,"status":"0","time":"","type":"text","pic_url":"","chat_id":key]
childRef.child(key!).setValue(message)
childRef1.child(key!).setValue(message)
Here's the code which is observing the change in the database
func setUpChats() {
//Sender
let channelSender = UserDefaults.standard.string(forKey:"uid")! + "-" + UserDefaults.standard.string(forKey:"receiverUID")!
let childRefSender = Database.database().reference().child("chat").child(UserDefaults.standard.string(forKey:"uid")!+"-"+UserDefaults.standard.string(forKey:"receiverUID")!).queryOrdered(byChild: channelSender).queryLimited(toLast:self.start_index)
childRefSender.keepSynced(true)
childRefSender.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.timearray = []
self.seenTime = []
self.seenStatus = []
self.messages = []
self.chat_ids = []
self.urls = []
for artists in snapshot.children.allObjects as! [DataSnapshot] {
let artistObject = artists.value as? [String: AnyObject]
print("artistObject:", artistObject!)
let key1 = artists.key
print("Key1:",key1) //other user channel id
let id = artistObject!["sender_id"]
let rece_id = artistObject!["receiver_id"] as! String
let chat_id = artistObject!["chat_id"] as! String
let name = artistObject!["sender_name"]
let text = artistObject!["text"] as! String
let time = artistObject!["timestamp"]
let status = artistObject!["status"] as! String
let seenTime = artistObject!["time"] as! String
let type = artistObject!["type"] as! String
let imgurl = artistObject!["pic_url"] as! String
if(type == "image") {
let imageView = AsyncPhotoMediaItem(withURL: URL(string: imgurl)!)
if id as? String == self.senderId {
imageView.appliesMediaViewMaskAsOutgoing = true
}
else {
imageView.appliesMediaViewMaskAsOutgoing = false
}
let message = JSQMessage(senderId:id as? String, displayName: name as? String, media: imageView)
self.messages.add(message!)
} else if(type == "text") {
let message = JSQMessage(senderId: id as? String , displayName: name as? String , text: text )
print("Mesage:",message!)
self.messages.add(message!)
}
print("Time:", time!)
let localTime = self.utcToLocal(dateStr: time as! String)
print("localTime:", localTime!)
self.timearray.add(localTime!)
self.seenTime.add(seenTime)
self.seenStatus.add(status)
self.type.add(type)
self.chat_ids.add(chat_id)
self.urls.add(imgurl)
if(self.senderId != id as? String ){
if(status == "0"){
let statusUpdateRef = Database.database().reference().child("chat").child(UserDefaults.standard.string(forKey:"uid")!+"-"+UserDefaults.standard.string(forKey:"receiverUID")!)
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy HH:mm:ss"
let result = formatter.string(from: date)
let fullNameArr = result.components(separatedBy: " ")
// let name = fullNameArr[0]
let surname = fullNameArr[1]
let dateFormatter1 = DateFormatter()
dateFormatter1.dateFormat = "HH:mm:ss"
let date1 = dateFormatter1.date(from:surname)
dateFormatter1.dateFormat = "hh:mm a"
let starting_time = dateFormatter1.string(from:date1!)
let message = ["sender_id":id as! String,"receiver_id":rece_id,"sender_name":name!,"text":text,"timestamp":time!,"status":"1","time":starting_time,"type":type,"pic_url":imgurl,"chat_id":chat_id] as [String : Any]
statusUpdateRef.child(key1).updateChildValues(message)
}
}
DispatchQueue.main.async {
self.collectionView.reloadData()
let indexPath = NSIndexPath(item: self.messages.count - 1, section: 0)
self.collectionView.scrollToItem(at: indexPath as IndexPath, at: .bottom, animated: true)
self.collectionView.hideActivityIndicator()
self.finishReceivingMessage()
}
}
} else {
self.collectionView.showActivityIndicator()
}
})
//Receiver
let channel1 = UserDefaults.standard.string(forKey:"receiverUID")! + "-" + UserDefaults.standard.string(forKey:"uid")!
let childRefReceiver = Database.database().reference().child("chat").child(UserDefaults.standard.string(forKey:"receiverUID")!+"-"+UserDefaults.standard.string(forKey:"uid")!).queryOrdered(byChild: channel1).queryLimited(toLast:self.start_index)
childRefReceiver.keepSynced(true)
childRefReceiver.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
for artists in snapshot.children.allObjects as! [DataSnapshot] {
let artistObject = artists.value as? [String: AnyObject]
let key1 = artists.key
print(key1)
let id = artistObject!["sender_id"]
let rece_id = artistObject!["receiver_id"] as! String
let chat_id = artistObject!["chat_id"] as! String
let name = artistObject!["sender_name"]
let text = artistObject!["text"]
let time = artistObject!["timestamp"]
let status = artistObject!["status"] as! String
let imgurl = artistObject!["pic_url"] as! String
let type = artistObject!["type"] as! String
if(self.senderId != id as? String ){
if(status == "0"){
let childRefUpdate = Database.database().reference().child("chat").child(UserDefaults.standard.string(forKey:"receiverUID")!+"-"+UserDefaults.standard.string(forKey:"uid")!)
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy HH:mm:ss"
let result = formatter.string(from: date)
let fullNameArr = result.components(separatedBy: " ")
let surname = fullNameArr[1]
let dateFormatter1 = DateFormatter()
dateFormatter1.dateFormat = "HH:mm:ss"
let date1 = dateFormatter1.date(from:surname)
dateFormatter1.dateFormat = "hh:mm a"
let starting_time = dateFormatter1.string(from:date1!)
let message = ["sender_id":id as! String,"receiver_id":rece_id,"sender_name":name!,"text":text!,"timestamp":time!,"status":"1","time":starting_time,"type":type,"pic_url":imgurl,"chat_id":chat_id] as [String : Any]
childRefUpdate.child(key1).updateChildValues(message)
}
}
}
Please help me out where I am going wrong or what I need to change in the structure.
Solution 1:[1]
It's really hard to say what's going wrong here without seeing the exact flow, but it sounds like you may not be updating UserDefaults.standard.string(forKey:"UserId")
when the signed in user changes, so be sure to use an auth state listener for that as shown in the first code snippet in the documentation on getting the current user.
It may also be that you're not be updating UserDefaults.standard.string(forKey:"receiverUserId")
when the user selects another recipient, so be sure to check that too.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Frank van Puffelen |