I am currently working on implementing Direct Messaging in my iOS Social Media App, and thus far I have it functioning for the most part. However, I have two issues that I haven't been able to fix:
1.) The current user (we'll call him user1) can see his own messages in the chat, but not those of the person he's messaging, and vice versa, when user2 logs in after user1 sends a message, the chat remains empty on user2's end.
2.) Each chat isn't unique, meaning that whatever message User1 sends to User2, gets shown in the convo between User1 and User3 as well rather than each chat having its own unique messages.
I am using UIKit, MessageKit, and Parse. The following code for anything regarding Messages is as follows:
//
// ChatViewController.swift
// VibeCheck
//
// Created by Omar Hegazy on 4/11/23.
//
import UIKit
import MessageKit
import InputBarAccessoryView
import Parse
struct Message: MessageType
{
var sender: SenderType
var messageId: String
var sentDate: Date
var kind: MessageKind
}
class ChatViewController: MessagesViewController, MessagesDataSource, MessagesLayoutDelegate, MessagesDisplayDelegate, InputBarAccessoryViewDelegate
{
let currentUser = PFUser.current()!
var otherUser: PFUser!
var messages = [MessageType]()
var inputBar = InputBarAccessoryView()
override func viewDidLoad() {
super.viewDidLoad()
// Set up the other user
let query = PFUser.query()
query?.whereKey("objectId", notEqualTo: currentUser.objectId!)
query?.findObjectsInBackground(block: { (objects, error) in
if let users = objects as? [PFUser], let user = users.first {
self.otherUser = user
// Retrieve previous messages from Parse
let messageQuery = PFQuery(className: "Message")
messageQuery.whereKey("sender", equalTo: self.currentUser)
messageQuery.whereKey("recipient", equalTo: self.otherUser!)
let recipientQuery = PFQuery(className: "Message")
recipientQuery.whereKey("sender", equalTo: self.otherUser!)
recipientQuery.whereKey("recipient", equalTo: self.currentUser)
let query = PFQuery.orQuery(withSubqueries: [messageQuery, recipientQuery])
query.findObjectsInBackground { (objects, error) in
if let messages = objects {
for message in messages {
let sender = message["sender"] as! PFUser
let text = message["text"] as! String
let sentDate = message.createdAt!
let messageKind = MessageKind.text(text)
let messageSender: Sender
do {
try sender.fetchIfNeeded()
messageSender = Sender(senderId: sender.objectId!, displayName: sender.username ?? "")
} catch {
messageSender = Sender(senderId: sender.objectId!, displayName: "Unknown")
print("Error fetching sender: \(error.localizedDescription)")
}
let message = Message(sender: messageSender, messageId: message.objectId!, sentDate: sentDate, kind: messageKind)
self.messages.append(message)
print("Fetched previous messages!")
}
self.messagesCollectionView.reloadData()
self.messagesCollectionView.scrollToLastItem(animated: false)
}
}
}
})
// Configure the messages collection view and input bar
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
view.addSubview(inputBar)
inputBar.delegate = self
inputBar.inputTextView.placeholder = "Type a message..."
inputBar.sendButton.setTitle("Send", for: .normal)
inputBar.sendButton.setTitleColor(view.tintColor, for: .normal)
inputBar.sendButton.addTarget(self, action: #selector(sendButtonPressed), for: .touchUpInside)
inputBar.translatesAutoresizingMaskIntoConstraints = false
inputBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
inputBar.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
inputBar.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
func currentSender() -> SenderType
{
return Sender(senderId: currentUser.objectId!, displayName: currentUser.username ?? "")
}
func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int
{
return messages.count
}
func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType
{
return messages[indexPath.section]
}
@objc func sendButtonPressed()
{
let messageText = inputBar.inputTextView.text.trimmingCharacters(in: .whitespacesAndNewlines)
guard !messageText.isEmpty else
{
return
}
let message = Message(sender: currentSender(), messageId: UUID().uuidString, sentDate: Date(), kind: .text(messageText))
messages.append(message)
inputBar.inputTextView.text = ""
messagesCollectionView.reloadData()
messagesCollectionView.scrollToLastItem(animated: true)
print("Message sent!")
// Save the message to Parse
let parseMessage = PFObject(className: "Message")
parseMessage["sender"] = currentUser
parseMessage["recipient"] = otherUser
parseMessage["text"] = messageText
parseMessage.saveInBackground()
print("Message saved!")
}
func inputBar(_ inputBar: InputBarAccessoryView, textViewTextDidChangeTo text: String)
{
if text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
{
inputBar.sendButton.isEnabled = false
} else
{
inputBar.sendButton.isEnabled = true
}
}
func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String)
{
sendButtonPressed()
}
}
and
import UIKit
import MessageKit
import Parse
class DirectMessagingViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{ var otherUsers = [PFUser]()
var currentUser: PFUser!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return otherUsers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = otherUsers[indexPath.row].username
cell.accessoryType = .disclosureIndicator
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// Show chat messages
let vc = ChatViewController()
vc.title = otherUsers[indexPath.row].username
navigationController?.pushViewController(vc, animated: true)
}
@IBOutlet var myTable: UITableView!
override func viewDidLoad()
{
super.viewDidLoad()
myTable.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
myTable.delegate = self
myTable.dataSource = self
// Get current user
currentUser = PFUser.current()
// Query for other users
let query = PFUser.query()
query?.whereKey("objectId", notEqualTo: currentUser.objectId!)
query?.findObjectsInBackground(block: { (users, error) in
if let users = users as? [PFUser] {
self.otherUsers = users
self.myTable.reloadData()
} else {
print("Error querying for users: \(error?.localizedDescription ?? "")")
}
})
}
}
DirectMessagingViewControler()
is what displays the users that you can chat with as well as the messages in each chat, while ChatViewController()
handles everything regarding sending and receiving messages as well as fetching them.
Edit: I realized that I accidentally uploaded DirectMessageViewController twice. I have fixed it and now you can see ChatViewController.