For my iOS Social Media App project, I am currently working on the Direct Messaging feature of the app. As of making this post, I have the basic functionality working where you can send messages to other users in the app. However, there are a few issues I am experiencing.
1.) Being able to see the other user’s texts and replies. I can send a message to the user but said user won’t be able to see it and vice versa for some reason I haven’t been able to fetch and load chat history properly.
2.) The chats don’t have their own unique messages. If I send a message to one user, it shows up in my chats with other users as well rather than each chat having its own unique messages.
They both seem to be issues related to fetching saved data from my Parse Cloud. The messages are saved properly to the cloud but fetching them and displaying them seems to be the issue.
Here is my code associated with Messaging:
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 ?? "")")
}
})
}
}
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]()
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
messageInputBar.delegate = self
messageInputBar.inputTextView.placeholder = "Type a message..."
messageInputBar.sendButton.setTitle("Send", for: .normal)
messageInputBar.sendButton.setTitleColor(view.tintColor, for: .normal)
messageInputBar.sendButton.addTarget(self, action: #selector(sendButtonPressed), for: .touchUpInside)
}
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 = messageInputBar.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)
messageInputBar.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 { (success, error) in
if success {
print("Message saved!")
} else if let error = error {
print("Error saving message: \(error.localizedDescription)")
}
}
}
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()
}
}
The DirectMessageViewController() loads all the users you can chat with and displays the conversation with a user when you tap on that user. The ChatViewController() is what handles the functionality regarding sending messages, saving them to the Parse Cloud, and fetching them. All help would be greatly appreciated!