I am using Firebase and JSQMessagesViewcontroller. I add some message to test but didn't show up. Can anyone help to see what I have missed? Thanks!
My Code:
import UIKit
import Firebase
import JSQMessagesViewController
class ChatViewController: JSQMessagesViewController {
var messages = [JSQMessage]()
lazy var outgoingBubbleImageView:JSQMessagesBubbleImage = self.setupOutgoingBubble()
lazy var incomingBubbleimageView:JSQMessagesBubbleImage = self.setupIncomingBubble()
override func viewDidLoad() {
super.viewDidLoad()
self.senderDisplayName = "cindy"
self.senderId = FIRAuth.auth()?.currentUser?.uid
print("sender id is: \(self.senderId)")
}
override func viewDidAppear(_ animated: Bool) {
addMessage(withId: "john", name: "doo", text: "hello, I am here")
addMessage(withId: senderId, name: "Me", text: "how are you")
addMessage(withId: senderId, name: "Me", text: "i am earlier than you.")
// I added finishReceivingMessage() here, but the app crashed.
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = messages[indexPath.item]
if message.senderId == senderId {
return outgoingBubbleImageView
} else {
return incomingBubbleimageView
}
}
func addMessage(withId id:String, name: String, text:String) {
if let message = JSQMessage(senderId: id, displayName: name, text: text) {
self.messages.append(message)
}
}
func setupOutgoingBubble() -> JSQMessagesBubbleImage {
let bubbleImagesFactory = JSQMessagesBubbleImageFactory()
return (bubbleImagesFactory?.outgoingMessagesBubbleImage(with: UIColor.jsq_messageBubbleBlue()))!
}
func setupIncomingBubble() -> JSQMessagesBubbleImage {
let bubbleImagesFactory = JSQMessagesBubbleImageFactory()
return (bubbleImagesFactory?.incomingMessagesBubbleImage(with: UIColor.jsq_messageBubbleLightGray()))!
}
}
So for starters I would suggest that you go and checkout the Develop branch of JSQMessagesVeiwController and pull up the SwiftExample contained within. This is the latest version and has been updated for swift3. Then I would isolate your issues. Since you are actually makeing the messages locally and not leveraging firebase I would Comment that out.
Then we can make your fake conversation like they do in the DemoConversation.swift file
let message = JSQMessage(senderId: "john", displayName: "doo", text: "hello, I am here")
let message2 = JSQMessage(senderId: "asdf", displayName: "Me", text: "how are you")
let message3 = JSQMessage(senderId: "asdf", displayName: "Me", text: "i am earlier than you.")
func makeNormalConversation() -> [JSQMessage] {
conversation = [message, message2, message3]
return conversation
}
Then you just need to make sure you have everything setup for you view and set your messages to makeNormalConversation()
Then for your ChatViewController the minimum amount of code is this:
//
// ChatViewController.swift
// SwiftExample
//
// Created by Dan Leonard on 5/11/16.
// Copyright © 2016 MacMeDan. All rights reserved.
//
import UIKit
import JSQMessagesViewController
class ChatViewController: JSQMessagesViewController {
var messages = [JSQMessage]()
let defaults = UserDefaults.standard
var conversation: Conversation = makeConversation() // Here is where your messages are added.
var incomingBubble: JSQMessagesBubbleImage!
var outgoingBubble: JSQMessagesBubbleImage!
fileprivate var displayName: String!
override func viewDidLoad() {
super.viewDidLoad()
incomingBubble = JSQMessagesBubbleImageFactory().incomingMessagesBubbleImage(with: UIColor.jsq_messageBubbleBlue())
outgoingBubble = JSQMessagesBubbleImageFactory().outgoingMessagesBubbleImage(with: UIColor.lightGray)
collectionView?.collectionViewLayout.incomingAvatarViewSize = .zero
collectionView?.collectionViewLayout.outgoingAvatarViewSize = .zero
automaticallyScrollsToMostRecentMessage = true
self.collectionView?.reloadData()
self.collectionView?.layoutIfNeeded()
}
// MARK: JSQMessagesViewController method overrides
override func didPressSend(_ button: UIButton, withMessageText text: String, senderId: String, senderDisplayName: String, date: Date) {
let message = JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: date, text: text)
self.messages.append(message)
self.finishSendingMessage(animated: true)
}
// MARK: JSQMessages CollectionView DataSource
override func senderId() -> String {
return "Cindy"
}
override func senderDisplayName() -> String {
return "Cindy Lue"
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, messageDataForItemAt indexPath: IndexPath) -> JSQMessageData {
return messages[indexPath.item]
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, messageBubbleImageDataForItemAt indexPath: IndexPath) -> JSQMessageBubbleImageDataSource {
return messages[indexPath.item].senderId == self.senderId() ? outgoingBubble : incomingBubble
}
}
Check out the example project on the develop branch for more details and let me know if you have more questions?
Then from this point you should add Firebase. You were on the right path for the senderID just make sure you actually get a unique id for the user that has logged in and then make sure it is the same ID for the messages sent by that user.
Good luck 🖖🏽
You must add finishReceivingMessage() after adding all of the example messages. This is sort of like a tableView.reloadData(), except for the JSQMessagesViewController. It also scrolls to newest message for you.
Related
I have followed used this link Raywenlich Firebase Tutorial Real Time Chat
1: https://www.raywenderlich.com/140836/firebase-tutorial-real-time-chat-2. for creating chat using firebase, i have followed all the procedure correctly but when i hit send button it shows me this error,
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Error! required method
not implemented in subclass. Need to implement
-[JSQMessagesViewController didPressSendButton:withMessageText:senderId:senderDisplayName:date:]'
This is my code for press button to send,
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
let itemRef = messageRef.childByAutoId() // 1
let messageItem = [ // 2
"senderId":Auth.auth().currentUser?.uid,
"senderName": "Hamza",
"text": text!,
]
itemRef.setValue(messageItem) // 3
JSQSystemSoundPlayer.jsq_playMessageSentSound() // 4
finishSendingMessage() // 5
}
The screen is this,
Chat view Controller class
import UIKit
import Firebase
import FirebaseDatabase
class ChatView: JSQMessagesViewController {
var ref: DatabaseReference!
var messages = [JSQMessage]()
private lazy var messageRef = Database.database().reference().child("messages")
private var newMessageRefHandle: DatabaseHandle?
lazy var outgoingBubbleImageView: JSQMessagesBubbleImage = self.setupOutgoingBubble()
lazy var incomingBubbleImageView: JSQMessagesBubbleImage = self.setupIncomingBubble()
//var senderId: NSString = ""
override func viewDidLoad() {
super.viewDidLoad()
senderId = (Auth.auth().currentUser?.uid as NSString?)! as String!
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSize.zero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSize.zero
// observeMessages()
// Do any additional setup after loading the view.
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
private func setupOutgoingBubble() -> JSQMessagesBubbleImage {
let bubbleImageFactory = JSQMessagesBubbleImageFactory()
return bubbleImageFactory!.outgoingMessagesBubbleImage(with: UIColor.jsq_messageBubbleBlue())
}
private func setupIncomingBubble() -> JSQMessagesBubbleImage {
let bubbleImageFactory = JSQMessagesBubbleImageFactory()
return bubbleImageFactory!.incomingMessagesBubbleImage(with: UIColor.jsq_messageBubbleLightGray())
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
return nil
}
private func addMessage(withId id: String, name: String, text: String) {
if let message = JSQMessage(senderId: id, displayName: name, text: text) {
messages.append(message)
}
}
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
let itemRef = messageRef.childByAutoId() // 1
let messageItem = [ // 2
"senderId":Auth.auth().currentUser?.uid,
"senderName": "Hamza",
"text": text!,
]
itemRef.setValue(messageItem) // 3
JSQSystemSoundPlayer.jsq_playMessageSentSound() // 4
finishSendingMessage() // 5
}
I building an app that allows users to communicate with an AWS Lex chatbot, however the problem is right now is when the user taps the button to begin their conversation with the bot it crashes and gives me a Thread 1:EXC_BAD_INSTRUCTION error on this line:
let configuration = AWSServiceConfiguration(region: botRegion!, credentialsProvider: AWSIdentityManager.default().credentialsProvider)
I would really like to know what is causing it to behave like this, as Xcode does not highlight any significant parts of the code.
Below is the what my code looks like in its entirety for those interested.
import Foundation
import UIKit
import Photos
import JSQMessagesViewController
import AWSLex
import AWSMobileHubHelper
let ClientSenderId = "Client"
let ServerSenderId = "Server"
class JeevesChatViewController: JSQMessagesViewController, JSQMessagesComposerTextViewPasteDelegate {
// The name of the bot.
var botName: String?
// The bot region
var botRegion: AWSRegionType?
// The bot alias
var botAlias: String?
// The messages communicated between the client and the server
var messages: [JSQMessage]?
// The interaction kit client
var interactionKit: AWSLexInteractionKit?
// The session attributes
var sessionAttributes: [AnyHashable: Any]?
// Contents of outgoing image
var outgoingBubbleImageData: JSQMessagesBubbleImage?
// Contents of incoming image
var incomingBubbleImageData: JSQMessagesBubbleImage?
// Used to store task completion source of iteraction kit
var textModeSwitchingCompletion: AWSTaskCompletionSource<NSString>?
// The client image
var clientImage: JSQMessagesAvatarImage?
// The bot image
var serverImage: JSQMessagesAvatarImage?
override func viewDidLoad() {
super.viewDidLoad()
// Make the initial setup for the bot.
// Error occurs here - fatal error: unexpectedly found nil while unwrapping an Optional value
let configuration = AWSServiceConfiguration(region: botRegion!, credentialsProvider: AWSIdentityManager.default().credentialsProvider)
// Setup interaction kit configuration
let botConfig = AWSLexInteractionKitConfig.defaultInteractionKitConfig(withBotName:botName!, botAlias: botAlias!)
// Disable automatic voice playback
botConfig.autoPlayback = false
// Register the interaction kit client
AWSLexInteractionKit.register(with: configuration!, interactionKitConfiguration: botConfig, forKey: botName!)
// Fetch and set the interaction kit client
self.interactionKit = AWSLexInteractionKit.init(forKey: botName!)
// Set the interaction kit delegate
// Remove 'as? AWSLexInteractionDelegate' statement later
self.interactionKit?.interactionDelegate = self
// Setup JSQMessagesViewController configuration
self.showLoadEarlierMessagesHeader = false
// Initialise the avatars for client and server here.
// Setup the default keyboard type.
self.inputToolbar.contentView?.textView?.keyboardType = UIKeyboardType.default
// Initialise the messages list
self.messages = [JSQMessage]()
// Set the colours for messages bubbles
let bubbleFactory = JSQMessagesBubbleImageFactory()
self.outgoingBubbleImageData = bubbleFactory?.outgoingMessagesBubbleImage(with: UIColor.jsq_messageBubbleGreen())
self.incomingBubbleImageData = bubbleFactory?.incomingMessagesBubbleImage(with: UIColor.jsq_messageBubbleBlue())
self.inputToolbar.contentView?.leftBarButtonItem = nil
self.senderDisplayName = "User"
self.senderId = ClientSenderId
}
// MARK: - JSQMessagesViewController delegate methods
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
let message = JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: date, text: text)
self.messages?.append(message!)
if let textModeSwitchingCompletion = textModeSwitchingCompletion {
textModeSwitchingCompletion.set(result: text as NSString)
self.textModeSwitchingCompletion = nil
}
else {
self.interactionKit?.text(inTextOut: text)
}
self.finishSendingMessage(animated: true)
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, messageDataForItemAt indexPath: IndexPath) -> JSQMessageData {
return self.messages![indexPath.item]
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, didDeleteMessageAt indexPath: IndexPath) {
// Nothing happens here
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, messageBubbleImageDataForItemAt indexPath: IndexPath) -> JSQMessageBubbleImageDataSource {
let message = self.messages![indexPath.item]
if (message.senderId == self.senderId) {
return self.outgoingBubbleImageData!
}
return self.incomingBubbleImageData!
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, avatarImageDataForItemAt indexPath: IndexPath) -> JSQMessageAvatarImageDataSource? {
let message = messages![indexPath.item]
if message.senderId == ClientSenderId {
return self.clientImage
}
return self.serverImage
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let messages = messages {
return messages.count
}
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = (super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell)
let msg = self.messages?[indexPath.item]
if !msg!.isMediaMessage {
if (msg?.senderId == self.senderId) {
cell.textView?.textColor = UIColor.black
}
else {
cell.textView?.textColor = UIColor.white
}
}
return cell
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, attributedTextForCellTopLabelAt indexPath: IndexPath) -> NSAttributedString? {
if indexPath.item % 3 == 0 {
let message = self.messages?[indexPath.item]
return JSQMessagesTimestampFormatter.shared().attributedTimestamp(for: message!.date)
}
return nil
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath) -> NSAttributedString? {
let message = self.messages?[indexPath.item]
// iOS 7 sender name labels
if (message?.senderId == self.senderId) {
return nil
}
if indexPath.item - 1 > 0 {
let previousMessage = self.messages?[indexPath.item - 1]
if (previousMessage?.senderId == message?.senderId) {
return nil
}
}
// Do not specify attributes to use the default values.
return NSAttributedString(string: message!.senderDisplayName)
}
override func collectionView(_ collectionView: JSQMessagesCollectionView, attributedTextForCellBottomLabelAt indexPath: IndexPath) -> NSAttributedString? {
return nil
}
func composerTextView(_ textView: JSQMessagesComposerTextView, shouldPasteWithSender sender: Any) -> Bool {
return true
}
}
// MARK: - Bot Interaction Kit
extension JeevesChatViewController: AWSLexInteractionDelegate {
func interactionKit(_ interactionKit: AWSLexInteractionKit, onError error: Error) {
print("Error occurred: \(error)")
}
func interactionKit(_ interactionKit: AWSLexInteractionKit, switchModeInput: AWSLexSwitchModeInput, completionSource: AWSTaskCompletionSource<AWSLexSwitchModeResponse>?) {
self.sessionAttributes = switchModeInput.sessionAttributes
DispatchQueue.main.async(execute: {
let message: JSQMessage
// Handle a successful transaction
if (switchModeInput.dialogState == AWSLexDialogState.readyForFulfillment) {
// Currently just displaying the slots return on ready for fulfillment
if let slots = switchModeInput.slots {
message = JSQMessage(senderId: ServerSenderId, senderDisplayName: "", date: Date(), text: "Slots:\n\(slots)")
self.messages?.append(message)
self.finishSendingMessage(animated: true)
}
} else {
message = JSQMessage(senderId: ServerSenderId, senderDisplayName: "", date: Date(), text: switchModeInput.outputText!)
self.messages?.append(message)
self.finishSendingMessage(animated: true)
}
})
// This can be expanded to take input from the user.
let switchModeResponse = AWSLexSwitchModeResponse()
switchModeResponse.interactionMode = AWSLexInteractionMode.text
switchModeResponse.sessionAttributes = switchModeInput.sessionAttributes
completionSource?.set(result: switchModeResponse)
}
func interactionKitContinue(withText interactionKit: AWSLexInteractionKit, completionSource: AWSTaskCompletionSource<NSString>) {
textModeSwitchingCompletion = completionSource
}
}
You haven't set your credentialsProvider properly.
AWSIdentityManager was never initialized to be called:
let configuration = AWSServiceConfiguration(region: botRegion!, credentialsProvider: AWSIdentityManager.default().credentialsProvider)
if you are using AWS for S3 bucket you should use incognito and thus
let cognitoIdentityPoolId = //"XYZ"
let cognitoUnauthRoleArn = //"XYZ"
let cognitoAuthRoleArn = //"XYZ"
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: #Wherever, identityPoolId: cognitoIdentityPoolId, unauthRoleArn: cognitoUnauthRoleArn, authRoleArn: cognitoAuthRoleArn, identityProviderManager: nil)
which you can then change your line to:
let configuration = AWSServiceConfiguration(region: botRegion!, credentialsProvider: credentialsProvider)
I downloaded a demo chat application that runs perfectly but when I implement it into my own app it crashes and the code is exactly the same. The app gives you the ability to create a chat room and my app works up to this point but when you click on the chat room name that now appears on the table I get the following error:
Assertion failure in -[Irish_League_Grounds.ChatViewController viewWillAppear:], /Users/ryanball/Desktop/Irish League
Grounds/Pods/JSQMessagesViewController/JSQMessagesViewController/Controllers/JSQMessagesViewController.m:277
2017-05-17 17:32:55.815 Irish League Grounds[20456:681491]
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid parameter not
satisfying: self.senderDisplayName != nil'
Here is my code for the two view controllers I'm segueing between:
import UIKit
import Firebase
enum Section: Int {
case createNewChannelSection = 0
case currentChannelsSection
}
class ChannelListViewController: UITableViewController {
// MARK: Properties
var senderDisplayName: String?
var newChannelTextField: UITextField?
private var channelRefHandle: FIRDatabaseHandle?
private var channels: [Channel] = []
private lazy var channelRef: FIRDatabaseReference = FIRDatabase.database().reference().child("channels")
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
title = "RW RIC"
observeChannels()
}
deinit {
if let refHandle = channelRefHandle {
channelRef.removeObserver(withHandle: refHandle)
}
}
// MARK :Actions
#IBAction func createChannel(_ sender: AnyObject) {
if let name = newChannelTextField?.text {
let newChannelRef = channelRef.childByAutoId()
let channelItem = [
"name": name
]
newChannelRef.setValue(channelItem)
}
}
// MARK: Firebase related methods
private func observeChannels() {
// We can use the observe method to listen for new
// channels being written to the Firebase DB
channelRefHandle = channelRef.observe(.childAdded, with: { (snapshot) -> Void in
let channelData = snapshot.value as! Dictionary<String, AnyObject>
let id = snapshot.key
if let name = channelData["name"] as! String!, name.characters.count > 0 {
self.channels.append(Channel(id: id, name: name))
self.tableView.reloadData()
} else {
print("Error! Could not decode channel data")
}
})
}
// MARK: Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let channel = sender as? Channel {
let chatVc = segue.destination as! ChatViewController
chatVc.senderDisplayName = senderDisplayName
chatVc.channel = channel
chatVc.channelRef = channelRef.child(channel.id)
}
}
// MARK: UITableViewDataSource
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let currentSection: Section = Section(rawValue: section) {
switch currentSection {
case .createNewChannelSection:
return 1
case .currentChannelsSection:
return channels.count
}
} else {
return 0
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reuseIdentifier = (indexPath as NSIndexPath).section == Section.createNewChannelSection.rawValue ? "NewChannel" : "ExistingChannel"
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
if (indexPath as NSIndexPath).section == Section.createNewChannelSection.rawValue {
if let createNewChannelCell = cell as? CreateChannelCell {
newChannelTextField = createNewChannelCell.newChannelNameField
}
} else if (indexPath as NSIndexPath).section == Section.currentChannelsSection.rawValue {
cell.textLabel?.text = channels[(indexPath as NSIndexPath).row].name
}
return cell
}
// MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if (indexPath as NSIndexPath).section == Section.currentChannelsSection.rawValue {
let channel = channels[(indexPath as NSIndexPath).row]
self.performSegue(withIdentifier: "ShowChannel", sender: channel)
}
}
}
Here is the second view controller:
import UIKit
import Photos
import Firebase
import JSQMessagesViewController
final class ChatViewController: JSQMessagesViewController {
// MARK: Properties
private let imageURLNotSetKey = "NOTSET"
var channelRef: FIRDatabaseReference?
private lazy var messageRef: FIRDatabaseReference = self.channelRef!.child("messages")
fileprivate lazy var storageRef: FIRStorageReference = FIRStorage.storage().reference(forURL: "gs://chatchat-871d0.appspot.com")
private lazy var userIsTypingRef: FIRDatabaseReference = self.channelRef!.child("typingIndicator").child(self.senderId)
private lazy var usersTypingQuery: FIRDatabaseQuery = self.channelRef!.child("typingIndicator").queryOrderedByValue().queryEqual(toValue: true)
private var newMessageRefHandle: FIRDatabaseHandle?
private var updatedMessageRefHandle: FIRDatabaseHandle?
private var messages: [JSQMessage] = []
private var photoMessageMap = [String: JSQPhotoMediaItem]()
private var localTyping = false
var channel: Channel? {
didSet {
title = channel?.name
}
}
var isTyping: Bool {
get {
return localTyping
}
set {
localTyping = newValue
userIsTypingRef.setValue(newValue)
}
}
lazy var outgoingBubbleImageView: JSQMessagesBubbleImage = self.setupOutgoingBubble()
lazy var incomingBubbleImageView: JSQMessagesBubbleImage = self.setupIncomingBubble()
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.senderId = FIRAuth.auth()?.currentUser?.uid
observeMessages()
// No avatars
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSize.zero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSize.zero
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
observeTyping()
}
deinit {
if let refHandle = newMessageRefHandle {
messageRef.removeObserver(withHandle: refHandle)
}
if let refHandle = updatedMessageRefHandle {
messageRef.removeObserver(withHandle: refHandle)
}
}
// MARK: Collection view data source (and related) methods
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = messages[indexPath.item] // 1
if message.senderId == senderId { // 2
return outgoingBubbleImageView
} else { // 3
return incomingBubbleImageView
}
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
let message = messages[indexPath.item]
if message.senderId == senderId { // 1
cell.textView?.textColor = UIColor.white // 2
} else {
cell.textView?.textColor = UIColor.black // 3
}
return cell
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
return nil
}
override func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForMessageBubbleTopLabelAt indexPath: IndexPath!) -> CGFloat {
return 15
}
override func collectionView(_ collectionView: JSQMessagesCollectionView?, attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath!) -> NSAttributedString? {
let message = messages[indexPath.item]
switch message.senderId {
case senderId:
return nil
default:
guard let senderDisplayName = message.senderDisplayName else {
assertionFailure()
return nil
}
return NSAttributedString(string: senderDisplayName)
}
}
// MARK: Firebase related methods
private func observeMessages() {
messageRef = channelRef!.child("messages")
let messageQuery = messageRef.queryLimited(toLast:25)
// We can use the observe method to listen for new
// messages being written to the Firebase DB
newMessageRefHandle = messageQuery.observe(.childAdded, with: { (snapshot) -> Void in
let messageData = snapshot.value as! Dictionary<String, String>
if let id = messageData["senderId"] as String!, let name = messageData["senderName"] as String!, let text = messageData["text"] as String!, text.characters.count > 0 {
self.addMessage(withId: id, name: name, text: text)
self.finishReceivingMessage()
} else if let id = messageData["senderId"] as String!, let photoURL = messageData["photoURL"] as String! {
if let mediaItem = JSQPhotoMediaItem(maskAsOutgoing: id == self.senderId) {
self.addPhotoMessage(withId: id, key: snapshot.key, mediaItem: mediaItem)
if photoURL.hasPrefix("gs://") {
self.fetchImageDataAtURL(photoURL, forMediaItem: mediaItem, clearsPhotoMessageMapOnSuccessForKey: nil)
}
}
} else {
print("Error! Could not decode message data")
}
})
// We can also use the observer method to listen for
// changes to existing messages.
// We use this to be notified when a photo has been stored
// to the Firebase Storage, so we can update the message data
updatedMessageRefHandle = messageRef.observe(.childChanged, with: { (snapshot) in
let key = snapshot.key
let messageData = snapshot.value as! Dictionary<String, String>
if let photoURL = messageData["photoURL"] as String! {
// The photo has been updated.
if let mediaItem = self.photoMessageMap[key] {
self.fetchImageDataAtURL(photoURL, forMediaItem: mediaItem, clearsPhotoMessageMapOnSuccessForKey: key)
}
}
})
}
private func fetchImageDataAtURL(_ photoURL: String, forMediaItem mediaItem: JSQPhotoMediaItem, clearsPhotoMessageMapOnSuccessForKey key: String?) {
let storageRef = FIRStorage.storage().reference(forURL: photoURL)
storageRef.data(withMaxSize: INT64_MAX){ (data, error) in
if let error = error {
print("Error downloading image data: \(error)")
return
}
storageRef.metadata(completion: { (metadata, metadataErr) in
if let error = metadataErr {
print("Error downloading metadata: \(error)")
return
}
if (metadata?.contentType == "image/gif") {
mediaItem.image = UIImage.gifWithData(data!)
} else {
mediaItem.image = UIImage.init(data: data!)
}
self.collectionView.reloadData()
guard key != nil else {
return
}
self.photoMessageMap.removeValue(forKey: key!)
})
}
}
private func observeTyping() {
let typingIndicatorRef = channelRef!.child("typingIndicator")
userIsTypingRef = typingIndicatorRef.child(senderId)
userIsTypingRef.onDisconnectRemoveValue()
usersTypingQuery = typingIndicatorRef.queryOrderedByValue().queryEqual(toValue: true)
usersTypingQuery.observe(.value) { (data: FIRDataSnapshot) in
// You're the only typing, don't show the indicator
if data.childrenCount == 1 && self.isTyping {
return
}
// Are there others typing?
self.showTypingIndicator = data.childrenCount > 0
self.scrollToBottom(animated: true)
}
}
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
// 1
let itemRef = messageRef.childByAutoId()
// 2
let messageItem = [
"senderId": senderId!,
"senderName": senderDisplayName!,
"text": text!,
]
// 3
itemRef.setValue(messageItem)
// 4
JSQSystemSoundPlayer.jsq_playMessageSentSound()
// 5
finishSendingMessage()
isTyping = false
}
func sendPhotoMessage() -> String? {
let itemRef = messageRef.childByAutoId()
let messageItem = [
"photoURL": imageURLNotSetKey,
"senderId": senderId!,
]
itemRef.setValue(messageItem)
JSQSystemSoundPlayer.jsq_playMessageSentSound()
finishSendingMessage()
return itemRef.key
}
func setImageURL(_ url: String, forPhotoMessageWithKey key: String) {
let itemRef = messageRef.child(key)
itemRef.updateChildValues(["photoURL": url])
}
// MARK: UI and User Interaction
private func setupOutgoingBubble() -> JSQMessagesBubbleImage {
let bubbleImageFactory = JSQMessagesBubbleImageFactory()
return bubbleImageFactory!.outgoingMessagesBubbleImage(with: UIColor.jsq_messageBubbleBlue())
}
private func setupIncomingBubble() -> JSQMessagesBubbleImage {
let bubbleImageFactory = JSQMessagesBubbleImageFactory()
return bubbleImageFactory!.incomingMessagesBubbleImage(with: UIColor.jsq_messageBubbleLightGray())
}
override func didPressAccessoryButton(_ sender: UIButton) {
let picker = UIImagePickerController()
picker.delegate = self
if (UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera)) {
picker.sourceType = UIImagePickerControllerSourceType.camera
} else {
picker.sourceType = UIImagePickerControllerSourceType.photoLibrary
}
present(picker, animated: true, completion:nil)
}
private func addMessage(withId id: String, name: String, text: String) {
if let message = JSQMessage(senderId: id, displayName: name, text: text) {
messages.append(message)
}
}
private func addPhotoMessage(withId id: String, key: String, mediaItem: JSQPhotoMediaItem) {
if let message = JSQMessage(senderId: id, displayName: "", media: mediaItem) {
messages.append(message)
if (mediaItem.image == nil) {
photoMessageMap[key] = mediaItem
}
collectionView.reloadData()
}
}
// MARK: UITextViewDelegate methods
override func textViewDidChange(_ textView: UITextView) {
super.textViewDidChange(textView)
// If the text is not empty, the user is typing
isTyping = textView.text != ""
}
}
// MARK: Image Picker Delegate
extension ChatViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
picker.dismiss(animated: true, completion:nil)
// 1
if let photoReferenceUrl = info[UIImagePickerControllerReferenceURL] as? URL {
// Handle picking a Photo from the Photo Library
// 2
let assets = PHAsset.fetchAssets(withALAssetURLs: [photoReferenceUrl], options: nil)
let asset = assets.firstObject
// 3
if let key = sendPhotoMessage() {
// 4
asset?.requestContentEditingInput(with: nil, completionHandler: { (contentEditingInput, info) in
let imageFileURL = contentEditingInput?.fullSizeImageURL
// 5
let path = "\(FIRAuth.auth()?.currentUser?.uid)/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/\(photoReferenceUrl.lastPathComponent)"
// 6
self.storageRef.child(path).putFile(imageFileURL!, metadata: nil) { (metadata, error) in
if let error = error {
print("Error uploading photo: \(error.localizedDescription)")
return
}
// 7
self.setImageURL(self.storageRef.child((metadata?.path)!).description, forPhotoMessageWithKey: key)
}
})
}
} else {
// Handle picking a Photo from the Camera - TODO
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion:nil)
}
}
The problem lies with this section of your code:
guard let senderDisplayName = message.senderDisplayName else {
assertionFailure()
return nil
}
assertionFailure() must link to another function that runs assert(false) or some equivalent to that. assert(_) is meant to verify that a particular parameter or comparison returns true, or if it does not, it will crash the app. The app will not crash if it is a production build (like those on the App Store) because asserts are meant for debugging purposes.
Basically, the guard statement is necessary to verify that message.senderDisplayName is unwrappable to some value (not nil). If message.senderDisplayName is nil, then there is no point in running the code below the guard and the contents of the guard should be run instead. assertionFailure() will crash the app during testing and during production it will be ignored. When it is ignored, nil will be returned for the function and it will continue on as nothing happened.
So i'm trying to implement one to one chat system in the app that am building and have been having some trouble. This is what i have so far.
Here is my code
Please help me i would also really appreciate any examples. Thank you
`
import UIKit
import JSQMessagesViewController
import Firebase
class MessagesViewController: JSQMessagesViewController {
var messages = [Message]()
var userImage = ""
var toUser: String!
var toUserImageUrl: String!
var avatars = Dictionary<String, UIImage>()
var outgoingBubbleImageView: JSQMessagesBubbleImage!
var incomingBubbleImageView: JSQMessagesBubbleImage!
var senderImageUrl: String!
var batchMessages = true
// var ref: Firebase!
// *** STEP 1: STORE FIREBASE REFERENCES
var messagesRef: FIRDatabaseReference!
private func setupBubbles() {
let factory = JSQMessagesBubbleImageFactory()
outgoingBubbleImageView = factory.outgoingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleBlueColor())
incomingBubbleImageView = factory.incomingMessagesBubbleImageWithColor(
UIColor.jsq_messageBubbleLightGrayColor())
}
override func viewDidLoad() {
super.viewDidLoad()
print(toUser)
inputToolbar.contentView.leftBarButtonItem = nil
automaticallyScrollsToMostRecentMessage = true
// self.collectionView.backgroundColor = UIColor.blackColor()
self.navigationItem.hidesBackButton = true
let newBackButton = UIBarButtonItem(title: "< Back", style: UIBarButtonItemStyle.Bordered, target: self, action: #selector(MessagesViewController.back(_:)))
self.navigationItem.leftBarButtonItem = newBackButton;
self.navigationController?.navigationItem.leftBarButtonItem?.tintColor = UIColor.whiteColor()
senderId = (senderId != nil) ? senderId : "Anonymous"
if let urlString = userImage as? NSString {
setupAvatarImage(senderId, imageUrl: senderImageUrl, incoming: false)
senderImageUrl = urlString as String
} else {
setupAvatarColor(senderDisplayName, incoming: false)
senderImageUrl = ""
}
senderId = Constants.currentUser
self.title = toUser
// No avatars
collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSizeZero
collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero
setupBubbles()
setupFirebase()
}
func back(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func setupFirebase() {
// *** STEP 2: SETUP FIREBASE
messagesRef = FIRDatabase.database().reference()
// *** STEP 4: RECEIVE MESSAGES FROM FIREBASE (limited to latest 25 messages)
messagesRef.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
let text = snapshot.value!["text"] as? String
let sender = snapshot.value!["from"] as? String
let toUsername = snapshot.value!["toUser"] as? String
let senderimageUrl = snapshot.value!["senderimageUrl"] as? String
let toUserImageUrl = snapshot.value!["toUserimageUrl"] as? String
let message = Message(text: text, sender: sender, toUsername: toUsername, senderimageUrl: senderimageUrl, toUserimageUrl: toUserImageUrl)
self.messages.append(message)
self.finishReceivingMessage()
})
}
func sendMessage(text: String!, sender: String!, to: String!) {
// *** STEP 3: ADD A MESSAGE TO FIREBASE
messagesRef.childByAutoId().setValue([
"text":text,
"toUser":toUser,
"from":self.senderId,
"senderimageUrl":senderImageUrl,
"toUserimageUrl":toUserImageUrl
])
}
func tempSendMessage(text: String!, sender: String!, to: String!) {
let message = Message(text: text, sender: sender, toUsername: to, senderimageUrl: senderImageUrl, toUserimageUrl: toUserImageUrl)
messages.append(message)
}
func setupAvatarImage(name: String, imageUrl: String?, incoming: Bool) {
if let stringUrl = imageUrl {
if let url = NSURL(string: stringUrl) {
if let data = NSData(contentsOfURL: url) {
let image = UIImage(data: data)
let diameter = incoming ? UInt(collectionView.collectionViewLayout.incomingAvatarViewSize.width) : UInt(collectionView.collectionViewLayout.outgoingAvatarViewSize.width)
let avatarImage = JSQMessagesAvatarImageFactory.avatarImageWithImage(image, diameter: diameter).avatarImage
avatars[name] = avatarImage
return
}
}
}
// At some point, we failed at getting the image (probably broken URL), so default to avatarColor
setupAvatarColor(name, incoming: incoming)
}
func setupAvatarColor(name: String, incoming: Bool) {
let diameter = incoming ? UInt(collectionView.collectionViewLayout.incomingAvatarViewSize.width) : UInt(collectionView.collectionViewLayout.outgoingAvatarViewSize.width)
let rgbValue = name.hash
let r = CGFloat(Float((rgbValue & 0xFF0000) >> 16)/255.0)
let g = CGFloat(Float((rgbValue & 0xFF00) >> 8)/255.0)
let b = CGFloat(Float(rgbValue & 0xFF)/255.0)
let color = UIColor(red: r, green: g, blue: b, alpha: 0.5)
let nameLength = name.characters.count
let initials : String? = name.substringToIndex(toUser.startIndex.advancedBy(min(3, nameLength)))
let userImage = JSQMessagesAvatarImageFactory.avatarImageWithUserInitials(initials, backgroundColor: color, textColor: UIColor.blackColor(), font: UIFont.systemFontOfSize(CGFloat(13)), diameter: diameter).avatarImage
avatars[name] = userImage
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
collectionView.collectionViewLayout.springinessEnabled = true
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// if ref != nil {
// ref.unauth()
// }
}
// ACTIONS
func receivedMessagePressed(sender: UIBarButtonItem) {
// Simulate reciving message
showTypingIndicator = !showTypingIndicator
scrollToBottomAnimated(true)
}
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!){
JSQSystemSoundPlayer.jsq_playMessageSentSound()
sendMessage(text, sender: senderId, to: toUser)
finishSendingMessage()
}
override func didPressAccessoryButton(sender: UIButton!) {
print("Camera pressed!")
}
override func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! {
return messages[indexPath.item]
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! {
let message = messages[indexPath.item] // 1
if message.senderId() == self.senderId{ // 2
return outgoingBubbleImageView
} else { // 3
return incomingBubbleImageView
}
}
override func collectionView(collectionView: JSQMessagesCollectionView!,
avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {
return nil
}
func collectionView(collectionView: JSQMessagesCollectionView!, bubbleImageViewForItemAtIndexPath indexPath: NSIndexPath!) -> UIImageView! {
let message = messages[indexPath.item]
if message.senderId() == self.senderId {
return UIImageView(image: outgoingBubbleImageView.messageBubbleImage, highlightedImage: outgoingBubbleImageView.messageBubbleHighlightedImage)
}
return UIImageView(image: incomingBubbleImageView.messageBubbleImage, highlightedImage: incomingBubbleImageView.messageBubbleHighlightedImage)
}
func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageViewForItemAtIndexPath indexPath: NSIndexPath!) -> UIImageView! {
let message = messages[indexPath.item]
if let avatar = avatars[message.senderId()] {
return UIImageView(image: avatar)
} else {
setupAvatarImage(message.senderId(), imageUrl: message.senderimageUrl(), incoming: true)
return UIImageView(image:avatars[message.senderId()])
}
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAtIndexPath: indexPath) as! JSQMessagesCollectionViewCell
let message = messages[indexPath.item]
if message.senderId() == self.senderId {
cell.textView.textColor = UIColor.whiteColor()
} else {
cell.textView.textColor = UIColor.blackColor()
}
let attributes : [String:AnyObject] = [NSForegroundColorAttributeName:cell.textView.textColor!, NSUnderlineStyleAttributeName: 1]
cell.textView.linkTextAttributes = attributes
// cell.textView.linkTextAttributes = [NSForegroundColorAttributeName: cell.textView.textColor,
// NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle]
return cell
}
// View usernames above bubbles
override func collectionView(collectionView: JSQMessagesCollectionView!, attributedTextForMessageBubbleTopLabelAtIndexPath indexPath: NSIndexPath!) -> NSAttributedString! {
let message = messages[indexPath.item];
// Sent by me, skip
if message.senderId() == self.senderId {
return nil;
}
// Same as previous sender, skip
if indexPath.item > 0 {
let previousMessage = messages[indexPath.item - 1];
if previousMessage.senderId() == message.senderId() {
return nil;
}
}
return NSAttributedString(string:message.senderId())
}
override func collectionView(collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForMessageBubbleTopLabelAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
let message = messages[indexPath.item]
// Sent by me, skip
if message.senderId() == self.senderId {
return CGFloat(0.0);
}
// Same as previous sender, skip
if indexPath.item > 0 {
let previousMessage = messages[indexPath.item - 1];
if previousMessage.senderId() == message.senderId() {
return CGFloat(0.0);
}
}
return kJSQMessagesCollectionViewCellLabelHeightDefault
}
}
`
So I would suggest checking out the
Swift Example can be found in the SwiftExample folder just open the SwiftExample.xcworkspace.
https://github.com/jessesquires/JSQMessagesViewController
it is apart of JSQMessagesViewController and is currently maintained.Also has an example of both a group and 1on1 conversation. That should help you to accomplish private conversations. But I think you are missing a key component which is your conversations view. That is where you would initiate a conversation. Then you should have an endpoint on your firebase backend that you can hit for all the conversations a user has had previously. That way they can open them again.
I am using JSQMessageviewcontroller for creating a chat cliet using parse. I am sucessfull in achieving everything except "My sent message is being added twice in the jsqmessage array". Althuogh I have checked if JSQMessage array already contains that message, it should not be added, but the condition is false in case of a sent message.
Here is my code:
import UIKit
import JSQMessagesViewController
import Parse
class MessageViewController: JSQMessagesViewController {
var chatWith : String?
var chattingWith = false
let incomingBubble = JSQMessagesBubbleImageFactory().incomingMessagesBubbleImageWithColor(UIColor(red: 10/255, green: 180/255, blue: 230/255, alpha: 1.0))
let outgoingBubble = JSQMessagesBubbleImageFactory().outgoingMessagesBubbleImageWithColor(UIColor.lightGrayColor())
var messages = [JSQMessage]()
var updateTimer = NSTimer()
let updateDelay = 2.0
var jsqMessages : [JSQMessage] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.setup()
updateTimer = NSTimer.scheduledTimerWithTimeInterval(updateDelay, target: self, selector: "update", userInfo: nil, repeats: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidDisappear(animated: Bool) {
updateTimer.invalidate()
}
func reloadMessagesView() {
self.collectionView?.reloadData()
}
}
//MARK - Setup
extension MessageViewController {
func setup() {
self.senderId = PFUser.currentUser()?.objectId!
self.senderDisplayName = PFUser.currentUser()?.username!
}
}
//MARK - Data Source
extension MessageViewController {
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.messages.count
}
override func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! {
let data = self.messages[indexPath.row]
return data
}
override func collectionView(collectionView: JSQMessagesCollectionView!, didDeleteMessageAtIndexPath indexPath: NSIndexPath!) {
self.messages.removeAtIndex(indexPath.row)
}
override func collectionView(collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! {
let data = messages[indexPath.row]
switch(data.senderId) {
case self.senderId:
return self.outgoingBubble
default:
return self.incomingBubble
}
}
override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {
return nil
}
}
//MARK - Toolbar
extension MessageViewController {
override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!) {
let message = JSQMessage(senderId: PFUser.currentUser()?.objectId!, senderDisplayName: PFUser.currentUser()?.username!, date: date, text: text)
print("sent messages = \(message)")
if !self.messages.contains(message) {
self.messages += [message]
}
self.sendMessageToParse(message , date: date)
self.finishSendingMessage()
}
override func didPressAccessoryButton(sender: UIButton!) {
}
}
//MARK - Syncano
extension MessageViewController {
func sendMessageToParse(message: JSQMessage , date : NSDate) {
let obj = PFObject(className: "Chat")
obj.setObject((PFUser.currentUser()?.username)!, forKey: "username")
obj.setObject(message.text!, forKey: "Text")
obj.setObject(chatWith!, forKey: "To")
obj.setObject(PFUser.currentUser()!, forKey: "user")
obj.setObject(date, forKey: "dateCreatedOn")
try! obj.save()
}
func update() {
let query = PFQuery(className: "Chat")
query.limit = 10
query.includeKey("user")
// query.whereKey("To", equalTo: chatWith!)
query.findObjectsInBackgroundWithBlock {
(results: [PFObject]?, error: NSError?) -> Void in
for i in results! {
if i.objectForKey("username") as? String == PFUser.currentUser()?.username! {
let message = self.jsqMessageFromParseMessage(i, senderId: (PFUser.currentUser()?.objectId!)!, senderDisplayName: i.objectForKey("username")! as! String)
print("received message : \(message)")
self.jsqMessageArray(message)
}else {
let message = self.jsqMessageFromParseMessage(i, senderId: i.objectForKey("To")! as! String, senderDisplayName: i.objectForKey("To")! as! String)
self.jsqMessageArray(message)
}
}
}
}
func jsqMessageFromParseMessage(message: PFObject , senderId : String , senderDisplayName : String) -> JSQMessage {
let jsqMessage = JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: message["dateCreatedOn"] as! NSDate, text: message["Text"] as! String)
return jsqMessage
}
func jsqMessageArray(jsqmessage : JSQMessage){
if self.messages.contains(jsqmessage) {
}else {
//
self.messages.append(jsqmessage)
self.finishReceivingMessage()
self.reloadMessagesView()
}
}
}
Will really appreciate if anyone can help. Thanks in advance.
There is a method that you can implement on JSQMessageData protocol called messageHash. It returns an Int and it is meant to be a unique id for each message. You should be able to use that to check and see if the message is the same or not.
You would need to create an object data class that adheres to the JSQMessageData protocol instead of using JSQMessage
class Message: JSQMessageData {
func senderId()-> String {
return //the result of currentUser()?.objectId
}
func senderDisplayName()-> String {
return //the result of currentUser()?.username!
}
func date()-> NSDate {
return // date
}
func isMediaMessage() -> Bool {
return false
}
func messageHash()-> String {
return //return a uniquely created id
}
func text()-> String {
return // text
}
}
You should be able to do this on PFUser, although I am not familiar enough with Parse to tell you if this is possible or not, but your really only re-mapping existing properties to be new names so it shouldn't be a problem