I am using firebase for chat app.when I try to send a message. than message, table got value but not update chat-message table. I refer to this link Here [https://github.com/DevSurya/ChatApp-Swift-And-Firebase].
when I run demo its working fine. Observe method is not calling in my case and collectionview is not refreshing
This is how I write data :
private func sendMessageWithProperty(_ property: [String: AnyObject]){
let ref = Database.database().reference().child("messages")
let childRef = ref.childByAutoId()
let toId = user!.id!
let fromId = Auth.auth().currentUser!.uid
let timeStamp = NSNumber.init(value: Date().timeIntervalSince1970)
var values: [String : AnyObject] = ["toId":toId as AnyObject, "fromId":fromId as AnyObject, "timeStamp":timeStamp]
values = values.merged(with: property)
childRef.updateChildValues(values)
childRef.updateChildValues(values) { (error, ref) in
if error != nil {
print(error!)
return
}
self.inputTextField.text = nil
let userMessageRef = Database.database().reference().child("user-messages").child(fromId).child(toId)
let messageId = childRef.key
userMessageRef.updateChildValues([messageId: 1])
let recipentUserMessageRef = Database.database().reference().child("user-messages").child(toId).child(fromId)
recipentUserMessageRef.updateChildValues([messageId: 1])
}
}
This is how I read data
func observeMessage() {
guard let uid = Auth.auth().currentUser?.uid, let toId = user?.id else {
return
}
let userMessageRef = Database.database().reference().child("user-messages").child(uid).child(toId)
userMessageRef.observe(.childAdded, with: { (snapshot) in
let messageId = snapshot.key
let messagesRef = Database.database().reference().child("messages").child(messageId)
messagesRef.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String: AnyObject] else {
return
}
let message = Message()
message.setValuesForKeys(dict)
self.messages.append(message)
DispatchQueue.main.async {
self.collectionView?.reloadData()
let indexpath = NSIndexPath.init(item: self.messages.count-1, section: 0)
self.collectionView?.scrollToItem(at: indexpath as IndexPath, at: .bottom, animated: true)
}
}, withCancel: nil)
}, withCancel: nil)
}
The ObserveMessage() function get automatically called when there is an update in database.but in my case its not get called. I think the problem is with user-messages table: it is not created when I send message.
Related
I have a bit of a lengthy question, So I apologize in advance I will try to illustrate this to the best of my abilities. I am trying to establish a notifications view controller that calls different types of data from Firebase and sets different notification types.
In the image above, this is how the cells should look when a user sends a notification to firebase. The user associated with that specific notification type as called and posted onto the screen.
In the firebase structure, We see that all of the information Stored is saved under the UID of the user in the first picture and is set under that specific users notification to show who is sending them a notification which is correct. These users names and images show perfectly as well as the image on the right.
The code I use to save this information is below,
fileprivate func saveSwipeToDataBase(didLike: Any) {
let swipeDate = Int(NSDate().timeIntervalSince1970)
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let cardUID = topCardView?.cardViewModel.uid else { return }
let documentData = ["workerId": uid,
"didLike": didLike,
"checked": 0,
"Swipe Date": swipeDate,
"type": SWIPE_INT_VALUE,
"posterId" : cardUID] as [String : Any]
self.postJobNotificationsIntoDatabseWithUID(uid: cardUID, values: documentData as [String : AnyObject])
}
private func postJobNotificationsIntoDatabseWithUID(uid: String, values: [String: AnyObject]) {
let ref = Database.database().reference(fromURL: "https://oddjobs-b131f.firebaseio.com/")
let usersReference = ref.child("notifications").child(uid).childByAutoId()
usersReference.setValue(values, withCompletionBlock: { (err, ref) in
if err != nil {
print("error saving data into firebase")
return
}
})
}
And below is how I retrieve this information and store it onto the Notifications View controller.
func fetchNotifications() {
guard let currentUID = Auth.auth().currentUser?.uid else { return }
NOTIFICATIONS_REF.child(currentUID).observeSingleEvent(of: .value) { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String, AnyObject> else { return }
print(dictionary)
for (_, postingRawData) in dictionary {
guard let postingDictionary = postingRawData as? Dictionary<String, AnyObject> else { continue }
guard let uid = postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["posterId"] as? String {
Database.fetchPoster(with: postId, completion: {(poster) in
let notification = userNotifications(user: user, poster: poster, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
})
} else {
let notification = userNotifications(user: user, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
}
})
}
}
}
Now that I got the correct way to setup up and show out of the way, I will show my enum and how I am distinguishing the different types of calls from firebase.
class userNotifications {
// MARK: - establish notificationTypes
enum NotificationType: Int, Printable {
case swipe
case accepted
case confirmed
case completed
case pay
var description: String {
switch self {
case .swipe: return " swiped on your Job "
case .accepted: return " accepted you to complete the job, "
case .confirmed: return " confirmed the job"
case .completed: return " completed the job"
case .pay: return " pay for completed"
}
}
init(index: Int) {
switch index {
case 0: self = .swipe
case 1: self = .accepted
case 2: self = .confirmed
case 3: self = .completed
case 4: self = .pay
default: self = .swipe
}
}
}
// MARK: - access firebaseData
var creationDate: Date!
var timeDate: Date!
var uid: String!
var fromId: String?
var workerId: String?
var user: User!
var poster: Poster!
var type: Int?
var notificationType: NotificationType!
var didCheck = false
init(user: User? = nil, poster: Poster? = nil, dictionary: Dictionary<String, AnyObject>) {
self.user = user
if let poster = poster {
self.poster = poster
}
if let swipeDate = dictionary["Swipe Date"] as? Double {
self.creationDate = Date(timeIntervalSince1970: swipeDate)
}
if let createDate = dictionary["creationDate"] as? Double {
self.creationDate = Date(timeIntervalSince1970: createDate)
}
if let swipeDate = dictionary["time&date"] as? Double {
self.timeDate = Date(timeIntervalSince1970: swipeDate)
}
if let type = dictionary["type"] as? Int {
self.notificationType = NotificationType(index: type)
}
if let uid = dictionary["uid"] as? String {
self.uid = uid
}
if let fromId = dictionary["fromId"] as? String {
self.fromId = fromId
}
if let workerId = dictionary["workerUID"] as? String {
self.workerId = workerId
}
if let checked = dictionary["checked"] as? Int {
if checked == 0 {
self.didCheck = false
} else {
self.didCheck = true
}
}
}
}
Above is the different types of notifications to be set.
Now, My issue is If I call a different notification type, such as .accepted, the information calls in a very different way.
The image above seems correct, However, the name and image are incorrect. it should be from the user ZacheryWilcox instead of Cjbwjdhbe. the user Cjbwjdhbe is the current user and the user who should be receing a notification from Zacherywilcox. not from itself.
In firebase, the information is saved as
the code I use to save this information is below
var workerUser: User? {
didSet {
let name = workerUser?.name
workerNameLabel.text = name
let workersUID = workerUser?.uid
workerNameLabel.text = name
guard let profileImage = workerUser?.profileImageUrl else { return }
workerImageView.loadImageUsingCacheWithUrlString(profileImage)
}
}
func saveUserData() {
let workUser = self.workerUser
guard let uid = Auth.auth().currentUser?.uid else { return }
let workerId = workUser?.uid
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else { return }
let user = User(dictionary: dictionary as [String : AnyObject])
workUser?.uid = snapshot.key
self.datePicker.datePickerMode = UIDatePicker.Mode.date
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd yyyy/ hh:mm a"
let selectedDate = dateFormatter.string(from: self.datePicker.date)
let creationDate = Int(NSDate().timeIntervalSince1970)
print(selectedDate)
let docData: [String: Any] = [
"workerId": workerId!,
"time&date": selectedDate,
"posterId" : uid,
"creationDate": creationDate,
"location": user.address!,
"type": 1,
"jobPost": "someUIDString",
"checked": 0,
]
self.postJobNotificationsIntoDatabseWithUID(uid: workerId!, values: docData as [String : AnyObject])
}, withCancel: { (err) in
print("attempting to load information")
})
print("Finished saving user info")
self.dismiss(animated: true, completion: {
print("Dismissal complete")
})
}
private func postJobNotificationsIntoDatabseWithUID(uid: String, values: [String: AnyObject]) {
let ref = Database.database().reference(fromURL: "https://oddjobs-b131f.firebaseio.com/")
let usersReference = ref.child("notifications").child(uid).childByAutoId()
usersReference.setValue(values, withCompletionBlock: { (err, ref) in
if err != nil {
print("error saving data into firebase")
return
}
})
}
When the type .accepted is being used to differentiate what notificationType is being called, the user who sent the notification is not being set correctly and I have no idea what is the reasoning behind this. The correct user that is sending this information over is Zacherywilcox, and that users image and name should be set to the user's notification screen. not the user Cjbe... I was wondering if anyone could help me fix these issues. Thank you in advance. I'm starting to think that the way I am saving the users information when accepting the user is incorrect.
When I am fetchingNotifications(), is it possible that since calling
guard let uid = postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["posterId"] as? String {
has an effect on whats going on? if so, Is there a way to differentiate between what notificationType is being called and fetch what notifications has been called with their respective users?
Just update your code to:
func fetchNotifications() {
guard let currentUID = Auth.auth().currentUser?.uid else { return }
NOTIFICATIONS_REF.child(currentUID).observeSingleEvent(of: .value) { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String, AnyObject> else { return }
print(dictionary)
let notificationId = snapshot.key
for (_, postingRawData) in dictionary {
guard let postingDictionary = postingRawData as? Dictionary<String, AnyObject> else { continue }
guard let type = postingDictionary["type"] as? Int else { continue }
guard let uid = (type == userNotifications.NotificationType.accepted.rawValue) ? postingDictionary["fromId"] as? String : postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["fromId"] as? String {
Database.fetchPoster(with: postId, completion: {(poster) in
let notification = userNotifications(user: user, poster: poster, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
})
} else {
let notification = userNotifications(user: user, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
}
// NOTIFICATIONS_REF.child(currentUID).child(notificationId).child("checked").setValue(1)
})
}
}
}
This will solve your problem.
I really want to know why the data is overwritten when the user types new data,
I want it to add more data to it not overwrite it the data
Also want to know how to read it
Thank you in advance
let oDB = Database.database().reference().child("Data")
let oDictionary = ["Data1" : strange.text! , "Data2" : stranger.text!]
let uid = Auth.auth().currentUser?.uid
oDB.child(uid!).setValue(oDictionary) {
(error, reference) in
if error != nil{
print(error!)
} else {
print("saved Sucessfully")
self.navigationController?.popViewController(animated: true)
}
}
//In another ViewController
func updateRequest() {
let uid = Auth.auth().currentUser?.uid
let yDb = Database.database().reference().child("Data").child(uid!)
postDb.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? Dictionary<String, String> {
let text = snapShotValue["Data1"]!
let case = snapShotValue["Data2"]!
let data = Data()
data.s= text
data.y = case
self.array.append(data)
self.table.reloadData()
}
}
}
setValue overwrites the old content , You may need childByAutoId
oDB.child(uid!).childByAutoId().setValue(oDictionary) {
(error, reference) in
if error != nil{
print(error!)
} else {
print("saved Sucessfully")
self.navigationController?.popViewController(animated: true)
}
This will give this structure
Data
> uid
> someKey1 <<<< auto generated
Data1:"---"
Data2:"---"
> someKey2 <<<< auto generated
Data1:"---"
Data2:"---"
Read
//In another ViewController
func updateRequest() {
let uid = Auth.auth().currentUser?.uid
let yDb = Database.database().reference().child("Data").child(uid!)
postDb.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? [String:[String:String]] {
Array(snapShotValue.values).forEach {
let data = Data()
data.s= $0["Data1"]!
data.y = $0["Data2"]!
self.array.append(data)
}
self.table.reloadData()
}
}
}
func uploadImage(){
let storage = Storage.storage()
let storageRef = storage.reference()
let uploadData = self.imageView.image!.jpegData(compressionQuality: 0.75)
let imagesRef = storageRef.child("images/myImage.jpg") //not sure how is it done
let uploadTask = imagesRef.putData(uploadData!, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
return
}
let size = metadata.size
imagesRef.downloadURL { (url, error) in
guard let downloadURL = url else {
return
}
}
}
}
func retrieveData(){
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
let userData = snapshot.value as? NSDictionary
print("Welcome back,", userData?["username"])
}) { (error) in
print(error.localizedDescription)
}
}
Hi, I'm looking for a way for user to upload image onto firebase based on their user id. Can anyone suggest how this can be achieved? Thanks in advance.
You can do something like this:
func sendMessageWithProperties(properties: [String: Any]) {
let ref = Database.database().reference().child("messages")
let childRef = ref.childByAutoId()
let toId = self.user?.id
let fromId = Auth.auth().currentUser?.uid
let timestamp: Int = Int(NSDate().timeIntervalSince1970)
var values: [String: Any] = ["toId": toId!,
"fromId": fromId!,
"timestamp": timestamp]
// To append other properties into values
properties.forEach({values[$0] = $1})
childRef.updateChildValues(values) { (error, ref) in
if error != nil {
print(error!)
return
}
let userMessageRef = Database.database().reference().child("user-messages").child(fromId!).child(toId!)
let messageId = childRef.key
userMessageRef.updateChildValues([messageId: 1])
let receiverMessageRef = Database.database().reference().child("user-messages").child(toId!).child(fromId!)
receiverMessageRef.updateChildValues([messageId: 1])
}
}
i have tried to create a chat message system but the old messages seems to still remain when the new messages are called.
Anyone can help? Furthermore, sometimes when a new user is created and he chat with another user.The messages from the other user is not reflected in the new user chat.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationItem.title = "Chat"
DispatchQueue.global(qos:.userInteractive).async {
DispatchQueue.main.async {
self.loadPosts()
self.loadPostsReceivedMessage()
}
}
}
//Get Message sent
func loadPosts() {
let senderIDNumber = Auth.auth().currentUser?.uid
let chatsRef = db.collection("chats").order(by: "timestamp", descending: false)
chatsRef.whereField("senderID", isEqualTo: senderIDNumber!).whereField("receiverID", isEqualTo: receiverIDNumber)
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
for document in documents {
let messageText = document.data()["message"] as? String
let senderIDNumber = document.data()["senderID"] as? String
let receiverIDNumber = document.data()["receiverID"] as? String
let timestamp = document.data()["timestamp"] as? String
guard let sender = document.data()["sender"] as? String else {return}
// let conversationsCounter = document.data()["conversationsCounter"] as? Int
guard let profileUrl = document.data()["profileUrl"] as? String else { return}
let chat = Chat(messageTextString: messageText!, senderIDNumber: senderIDNumber!, receiverIDNumber: receiverIDNumber!, timeStampString: timestamp!, profileImageUrl: profileUrl, senderString: sender)
self.chats.append(chat)
print(self.chats)
self.collectionView.reloadData()
}
}
}
//Get message received
func loadPostsReceivedMessage() {
/* let uid = Auth.auth().currentUser?.uid
let ref = Database.database().reference()
ref.child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
if let dic = snapshot.value as? [String: AnyObject]{
let currentUser = dic["username"] as? String
let senderIDNumber = Auth.auth().currentUser?.uid
} */
let chatsRef = db.collection("chats").order(by: "timestamp", descending: false)
print("thecurrentreceiver"+senderString)
print("thecurrentsender"+receiverIDNumber)
chatsRef.whereField("receiverID", isEqualTo: senderString).whereField("sender", isEqualTo: receiverIDNumber)
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
for document in documents {
let messageText = document.data()["message"] as? String
let senderIDNumber = document.data()["senderID"] as? String
let receiverIDNumber = document.data()["receiverID"] as? String
let timestamp = document.data()["timestamp"] as? String
// let conversationsCounter = document.data()["conversationsCounter"] as? Int
guard let profileUrl = document.data()["profileUrl"] as? String else { return}
guard let sender = document.data()["sender"] as? String else {return}
let chat = Chat(messageTextString: messageText!, senderIDNumber: senderIDNumber!, receiverIDNumber: receiverIDNumber!, timeStampString: timestamp!,profileImageUrl: profileUrl, senderString: sender)
print("whatisthemessage"+messageText!)
self.chats.append(chat)
print(self.chats)
self.chats.sort{$0.timestamp < $1.timestamp}
self.collectionView.reloadData()
}
}
}
You're using addSnapshotListener, which means that your callback gets called every time something relevant in the database changes. And when that happens, you loop over all the documents that match your query and add them to your view. This means that if there are multiple changes, you're adding most messages multiple times.
There are two common solutions:
Clear the view every time your callback gets called.
Only modify the view for the changes each time your callback gets called.
We'll use #2 below, since it is more efficient. Note that I'm only handling new messages to keep things simple. As you make your app more complete, you will also need to handle other types of changes, e.g. when a user deletes or updates a chat message.
let chatsRef = db.collection ("chats").order (by: "timestamp", descending: false)
chatsRef.whereField ("senderID", isEqualTo: senderIDNumber!)
.whereField ("receiverID", isEqualTo: receiverIDNumber)
.addSnapshotListener {
querySnapshot,
error in guard let documentChanges = querySnapshot?.documentChanges else {
print ("Error fetching documents: \(error!)")
return
}
for documentChange in documentChanges {
if (documentChange.type == .added) {
let data = documentChange.document.data ()
let messageText = data["message"] as? String
let senderIDNumber = data["senderID"] as? String
let receiverIDNumber = data["receiverID"] as? String
let timestamp = data["timestamp"] as? String
...
let chat = Chat (
messageTextString : messageText!,
senderIDNumber : senderIDNumber!,
receiverIDNumber : receiverIDNumber!,
timeStampString : timestamp!,
profileImageUrl : profileUrl,
senderString : sender
)
self.chats.append (chat)
print (self.chats)
self.collectionView.reloadData ()
}
}
}
For some more on this, have a look at responding to changes in the Firebase documentation.
I have a node in Firebase database. its a node for the messaging portion in my App. It is a private chat. I want to incorporate the ability for one user to delete his messages but the messages still remain in the other user's app. so I created a fan out property.
override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {
let convoRef = FIRDatabase.database().reference().child("messages").child(convoId!)
let itemRef = rootRef.child("messages").child(convoId!).childByAutoId() // 1
let messageItem = [ // 2
"senderId": senderId!,
"ReceiverId": senderDisplayName!,
"text": text!,
"timestamp": NSDate().timeIntervalSince1970,
"convoId": convoId!
] as [String : Any]
// itemRef.setValue(messageItem) // 3
itemRef.updateChildValues(messageItem) { (error, convoRef) in
if error != nil{
print(error)
return
}
let userMessagesRef = FIRDatabase.database().reference().child("user-messages").child(self.senderId!)
let messageId = itemRef.key
userMessagesRef.updateChildValues([messageId: 1])
let userMessagesRefs = FIRDatabase.database().reference().child("user-messages").child(self.senderDisplayName!)
let messagedId = itemRef.key
userMessagesRefs.updateChildValues([messagedId: 1])
}
}
And then i created the option to delete the message cell.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard let uid = FIRAuth.auth()?.currentUser?.uid else {
return
}
let ref = FIRDatabase.database().reference().child("user-messages").child(uid)
ref.observe(.childAdded, with: { (snapshot) in
self.messageId = snapshot.key
FIRDatabase.database().reference().child("user-messages").child(uid).child( self.messageId!).removeValue(completionBlock: { (error, ref) in
if error != nil {
print("error \(error)")
}else{
}
})})
self.messagesDictionary.removeValue(forKey: uid)
self.handleReloadTable()
}
But when the table reloads,it crashes
func loadData()
{
guard let uid = FIRAuth.auth()?.currentUser?.uid else {
return
}
let ref = FIRDatabase.database().reference().child("user-messages").child(uid)
ref.observe(.childAdded, with: { (snapshot) in
self.messageId = snapshot.key
})
FIRDatabase.database().reference().child("messages").observeSingleEvent(of: .value, with: { (snapshot:FIRDataSnapshot) in
if let postsDictionary = snapshot .value as? [String: AnyObject] {
for post in postsDictionary {
let messages = post.value as! [String: AnyObject]
for (id, value) in messages {
let info = value as! [String: AnyObject]
let convoId = info["convoId"]
let toId = info["ReceiverId"] as! String!
let fromId = info["senderId"] as! String!
if (toId == self.loggedInUserUid || fromId == self.loggedInUserUid) {
let ref = FIRDatabase.database().reference().child("messages").child(convoId as! String).child(self.messageId!)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let message = Message(dictionary: dictionary)
// self.messages.append(message)
if let receiver = message.convoId {
self.messagesDictionary[receiver] = message
self.messages = Array(self.messagesDictionary.values)
print(self.messages)
self.messages.sort(by: { (message1, message2) -> Bool in
return (message1.timestamp?.int32Value)! > (message2.timestamp?.int32Value)!
})
}
//this will crash because of background thread, so lets call this on dispatch_async main thread
DispatchQueue.main.async(execute: {
self.MessageTableView.reloadData()
})
}
}, withCancel: nil)}
}
}}})
}
And says messageId is an optional value.