'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

Chat Structure

So it is like

  • Chat
    • USER-A-ID-USER-B-ID
      • RandomID
        • Chat Data

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