0
votes

When Users have a conversation, I want the conversations to be displayed in a tableViewController. For example, if User1 sends 10 messages to User2, then I should have one cell in my TableView that displays the last message sent in User1 - User2 conversation. Again, if User1 sends User3 4 messages then User1 should have two tableView cells and User2 and User3 should only have 1 message. My database looks like this:

messages
      hx1DXeMFRU
         -KkVUFuhv7xsYVhnjldV
              ReceiverId: "hx1DX1hW5JZMSmDjwQRUYo3i5Yt2"
              senderId: "eMFRUhfmckTq9cRWNnwIc9mqxxv1"
              text:"hey"
              convoId: hx1DXeMFRU
              timestamp: 1495193736.860723
          -KkVUJG8VPOijCvzMKRf
              ReceiverId: "eMFRUhfmckTq9cRWNnwIc9mqxxv1"
              senderId: "hx1DX1hW5JZMSmDjwQRUYo3i5Yt2"
              text:"Hey how are you?"
              convoId: hx1DXeMFRU
              timestamp: 1495193699.448048

I am using the below function to retrieve all messages and view them in a messagesViewController. However, when I try to append the conversation between two unique users, it instead appends all of the messages into one cell. So in my specific example, I should have two cells, one for User1 communicating with User2 and User1 communicating with User3.

import UIKit
import Firebase
import FirebaseStorage
import FirebaseAuth
import FBSDKCoreKit
import FBSDKLoginKit
import FirebaseDatabase
import JSQMessagesViewController


class MessagesTableViewController: UITableViewController {

    var databaseRef = FIRDatabase.database().reference()
    var loggedInUser = FIRAuth.auth()?.currentUser
    var loggedInUserUid = FIRAuth.auth()?.currentUser?.uid
    var dictDetails: [String:AnyObject]?
    var messages = NSMutableArray()
    var messagesArray = [String]()
    var convoId: String?
    var receiverData: AnyObject?
    let storage = FIRStorage.storage()

    var senderId = FIRAuth.auth()?.currentUser?.uid

    @IBOutlet var MessageTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationController?.navigationBar.barTintColor = UIColor(red:0.23, green:0.73, blue:1.00, alpha:1.0)
        self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]

        self.navigationItem.title = "Messages"
        self.navigationItem.rightBarButtonItem?.tintColor = UIColor.white
        self.navigationItem.leftBarButtonItem?.tintColor = UIColor.white


        loadData()

    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    func loadData()
    {
        let uidToFind = loggedInUserUid as String!
        FIRDatabase.database().reference().child("messages")
            .queryOrdered(byChild: "ReceiverId")
            .queryEqual(toValue: uidToFind).queryLimited(toLast: 1)
            .observe(.childAdded, with: { snapshot in

                let msgDict = snapshot.value as! [String: Any]
                let msg = msgDict["ReceiverId"] as! String
                self.messages = []
                self.messages.append(msg)
                self.MessageTableView.reloadData()
            })
    }

      // MARK: - Table view data source

     override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if messages.count > 0 {
            tableView.backgroundView = .none;
            tableView.separatorStyle  = .singleLine
            return self.messages.count
        }
        else{
            let noDataLabel: UILabel     = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
            noDataLabel.text          = "No Messages"
            noDataLabel.textColor     = UIColor.lightGray
            noDataLabel.textAlignment = .center
            tableView.backgroundView  = noDataLabel
            tableView.separatorStyle  = .none
            return 0
        }
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessageTableViewCell


        //Configure the cell


        print(messages[indexPath.row])
        let message = self.messages[indexPath.row] as! [String: AnyObject]
        if let seconds = message["timestamp"] as? Double {
            let timeStampDate = NSDate(timeIntervalSince1970: seconds)
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "hh:mm a"
            cell.Time.text = dateFormatter.string(from: timeStampDate as Date)
        }



        cell.Message.text = message["text"] as? String


      if let imageName =  message["ReceiverId"] as? String {

            let ref = FIRDatabase.database().reference().child("posts").child(imageName)

        ref.observeSingleEvent(of: .value, with: { (snapshot)
            in

            if let dictionary = snapshot.value as? [String: AnyObject]{
                for post in dictionary {
                    let messages = post.value as! [String: AnyObject]
                    for (id, value) in messages {

                    cell.SellerName.text = messages["username"] as? String
                        if (messages["uid"] as? String) != nil {
                            let uidPoster = messages["uid"] as? String
                            let imageRef = FIRStorage.storage().reference().child((uidPoster)!+"/profile_pic.jpg")

                            imageRef.data(withMaxSize: 25 * 1024 * 1024, completion: { (data, error) -> Void in
                                if error == nil {

                                    let image = UIImage(data: data!)
                                    cell.UserPic.image = image

                                    cell.UserPic.layer.cornerRadius = 30
                                    cell.UserPic.clipsToBounds = true



                                }else {

                                }})}
                                          self.messages.add(value)
                    }}}})}

        return cell
    }
}
1
Please update the question with your Firebase structure as text, not images. Images are not searchable and if we need to use it in an answer, we have to retype it. You can get the structure from your Firebase Console->Three dots on right->Export JSON. - Jay
It's unclear what I try to append the conversation between two unique users means. Why are you appending? Why are you appending to? Once it's appended, what should happen? Why are you loading all of the conversations - if there were 200 it may be an overwhelming UI experience for the user. Please clarify your question and we'll take a look! - Jay
If User1 and User2 have a conversation, the texts are sent to firebase in the structure I provided above. If those two users send 20 messages among themselves, then I will like to get the last message sent between those two users and display it in a tableviewcell. If one of the User1 sends 4 messages to another user3, then I would like again to get the last message sent between those users and display it in the tableview. So now user1 has two tableview cells, user2 has 1 and User3 has 1 tableviewcell. @Jay - juelizabeth
If you are only interested in the last message, then why append messages? You only want the last one so a new message would replace an existing message in the UI. Is that correct? - Jay
yes, you are correct. I was appending the messages because I found a tutorial that was doing that so I assumed you have to append the messages in order to get the last message sent @Jay - juelizabeth

1 Answers

0
votes

The problem with the code in the question is this

queryEqual(toValue: "senderId")

That query is going to query the messages/ReceiverId nodes that have a value of "senderId" (literally the string senderId). It needs to query for the actual uid instead.

func loadData()
{
   let uidToFind = "hx1DX1hW5JZMSmDjwQRUYo3i5Yt2"
   FIRDatabase.database().reference().child("messages")
                         .queryOrdered(byChild: "ReceiverId")
                         .queryEqual(toValue: uidToFind)
                         .observe(.childAdded, with: { snapshot in

      let msgDict = snapshot.value as! [String: Any]
      let msg = msgDict["ReceiverId"] as! String
      self.messages = []
      self.messages.append(msg)
      self.MessageTableView.reloadData()
   })
}

I will stress that while this code works, it's iterating over all of the child nodes matching ReceiverId = uid, loading them one at a time and leaving an observer for any new child nodes added afterwards.

This is a good thing as it allows you to populate an array with multiple values as well as attaching a listener for new values.

However, if you are really only interested in the most recent post, a better direction would be to use queryLimitedToLast(1) which will only return the last node that matches the query.

Something like

var messages = [String]()
func loadData()
{
   let uidToFind = "hx1DX1hW5JZMSmDjwQRUYo3i5Yt2"
   FIRDatabase.database().reference().child("messages")
                         .queryOrdered(byChild: "ReceiverId")
                         .queryEqual(toValue: uidToFind)
                         .queryLimited(toLast: 1)
                         .observe(.childAdded, with: { snapshot in

      let msgDict = snapshot.value as! [String: Any]
      let msg = msgDict["ReceiverId"] as! String
      self.messages = []
      self.messages.append(msg)
      //self.MessageTableView.reloadData()
      print(self.messages) //this will print the last msg and any new msgs
   })
}