I implemented chat using MessageKit and Firebase and was wondering how to implement notifications whenever the user receives a message, I have set up my permissions but I'm having trouble figuring out knowing when a new message is sent. Any ideas would be appreciated.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let content = UNMutableNotificationContent()
databaseListener = channelRef?.order(by:"time").addSnapshotListener() { (querySnapshot, error) in
if let error = error {
print(error)
return
}
querySnapshot?.documentChanges.forEach() { diff in
if diff.type == .added {
let snapshot = diff.document
let id = snapshot.documentID
let senderId = snapshot["senderId"] as! String
let senderName = snapshot["senderName"] as! String
let text = snapshot["text"] as! String
let timestamp = snapshot["time"] as! Timestamp
let date = sentTimestamp.dateValue()
let sender = Sender(id: senderId, name: senderName)
let message = ChatMessage(sender: sender, messageId: id, sentDate: date, message: text)
self.messagesList.append(message)
// Notification
content.title = "New message"
content.body = "Tap to respond"
content.categoryIdentifier = AppDelegate.CATEGORY_IDENTIFIER
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
let request = UNNotificationRequest(identifier: ChatMessagesViewController.NOTIFICATION_IDENTIFIER, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
self.messagesCollectionView.insertSections([self.messagesList.count-1])
}
}
}
}
Related
I'm trying to trigger local notifications for every 1 minute even in terminated state and background state. But, my app should not trigger notification when time is after 06:00PM. Below is my code.
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60,repeats: true)
//UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
// Create the request
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString,
content: content, trigger: trigger)
// Schedule the request with the system.
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if error != nil {
// Handle any errors.
}
}
}
To handle local notification, try this:
public func addLocalNotification(hour: Int, minute: Int, identifier: String, title: String, body: String) {
// Initialize
let center = UNUserNotificationCenter.current()
// Set its content
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
// whenever you want to notify user select time
var dateComp = DateComponents()
dateComp.calendar = Calendar.current
dateComp.hour = hour
dateComp.minute = minute
// repeat and date
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComp, repeats: true)
// Initializing the Notification Request object
let req = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
// Add the notification to the center
center.add(req) { (error) in
if (error) != nil {
print(error!.localizedDescription)
}
}
}
When my app is killed I am not receiving push notification, but is working fine with the background. After receiving notification I am generating a local notification by getting data from my realm DB. In payload content available key is 1. Please suggest me where I am going wrong, with getting fcm notification or running in the background.
here is my code -
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let state: UIApplicationState = UIApplication.shared.applicationState
print(userInfo)
if state == .active{
let messageBody = userInfo["aps"] as! Dictionary<String,Any>
print("############# got notification")
if let message = messageBody["message"] {
let messageDict = DCUtilityClass.convertToDictionary(text: message as! String)
let _ = DCPushNotificationAPI.getNotificationResult(info: (messageDict)!)
}
} else if state == .inactive {
let messageBody = userInfo["aps"] as! Dictionary<String,Any>
print("############# got notification")
if let message = messageBody["message"] {
let messageDict = DCUtilityClass.convertToDictionary(text: message as! String)
let _ = DCPushNotificationAPI.getNotificationResult(info: (messageDict)!)
}
} else if state == .background {
let messageBody = userInfo["aps"] as! Dictionary<String,Any>
print("############# got notification")
if let message = messageBody["message"] {
let messageDict = DCUtilityClass.convertToDictionary(text: message as! String)
let _ = DCPushNotificationAPI.getNotificationResult(info: (messageDict)!)
}
}
completionHandler(UIBackgroundFetchResult.noData)
//receiveMessageMethod(recieveMessage: messageBody as! Dictionary<String, Any>)
}
and
class func triggerLocalPushNotification(title:String, body:Dictionary<String,Any>) {
//creating the notification content
let content = UNMutableNotificationContent()
//adding title, subtitle, body and badge
let messageDictModel = DCChatListHistoryModel.init().initWithUserChatDictionary(userDictionary: body)
if messageDictModel.mIsGroup{
if messageDictModel.mGroupDetailsModelArray.count == 0 {
let groupData = DCDBDataUtilityClass.getGroupInfoByConversationId(id: messageDictModel.m_id)
content.title = (groupData?.mGroupsModelArray.first?.mGroup_name)!
content.body = messageDictModel.mlatest_message
} else {
content.title = (messageDictModel.mGroupDetailsModelArray.first?.mGroup_name)!
content.body = messageDictModel.mlatest_message
}
} else {
var messageSender = ""
if messageDictModel.mMessageFrom == UserDefaults.standard.value(forKey: "user_id") as! String {
messageSender = messageDictModel.mMessage_to
} else {
messageSender = messageDictModel.mMessageFrom
}
let name = DCDBDataUtilityClass.getInfoById(id: messageSender)
content.title = (name?.mFull_name)!
content.body = messageDictModel.mlatest_message
}
//getting the notification trigger
//it will be called after 5 seconds
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.2, repeats: false)
//getting the notification request
let request = UNNotificationRequest(identifier: "iOSNotification", content: content, trigger: trigger)
//adding the notification to notification center
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
I am working on a medicine reminder app. The user will input two dates and I need to schedule local notifications between those two dates. I can't find how to specify the starting and the ending date in the UNNotificationRequest.
I have tried this:
static func setAlarmWithDate (hour:Int, minutes:Int , day:Int, medicineName:String, medicineId:String) {
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "MY_MEDICINE".localizedString(), arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "This is a notification for your medicine \(medicineName)",
arguments: nil)
content.sound = UNNotificationSound.default()
var userInfo = [String:String]()
let medicine = medicineId
userInfo["medicine"] = medicine
content.userInfo = userInfo
var dateInfo = DateComponents()
dateInfo.hour = hour
dateInfo.minute = minutes
dateInfo.weekday = day
let trigger = UNCalendarNotificationTrigger(dateMatching: dateInfo, repeats: true)
let identifier = medicineId
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
}
}
}
I am new to Swift and I am really stuck.
Everything was working fine, then I deleted some old messages and conversations from My Firebase Database. Now every time I send a message I get a crash. I deleted all old users and created new users and tried to send messages and I still keep getting a crash. I am not sure what can be causing this. Any suggestions will be helpful. It first happened after I tested out this function to delete the table cell...
func deleteConversation(_ conversation:Conversation) {
guard let user = Auth.auth().currentUser else { return }
let ref = Database.database().reference()
let obj = [
"conversations/users/\(user.uid)/\(conversation.partner_uid)/muted": true
] as [String:Any]
print("OBBJ: \(obj)")
ref.updateChildValues(obj, withCompletionBlock: { error, ref in
if error != nil {
let alert = UIAlertController(title: "Error deleting conversation!", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
} else {
let alert = UIAlertController(title: "Conversation deleted!", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
}
})
}
func downloadMessages() {
self.messages = []
downloadRef?.observe(.childAdded, with: { snapshot in
let dict = snapshot.value as! [String:AnyObject]
if let sender = dict["sender"] as! String!, let recipient = dict["recipient"] as! String!, let text = dict["text"] as! String!, text.characters.count > 0 {
let timestamp = dict["timestamp"] as! Double
let date = NSDate(timeIntervalSince1970: timestamp/1000)
let message = JSQMessage(senderId: sender, senderDisplayName: "", date: date as Date!, text: text)
self.messages.append(message!)
self.reloadMessagesView()
self.finishReceivingMessage(animated: true)
}
else if let id = dict["sender"] as! String!,
let photoURL = dict["imageUrl"] as! String!, photoURL.characters.count > 0 { // 1
// 2
if let mediaItem = JSQPhotoMediaItem(maskAsOutgoing: id == self.senderId) {
// 3
let timestamp = dict["timestamp"] as! Double
let date = NSDate(timeIntervalSince1970: timestamp/1000)
if let message = JSQMessage(senderId: id, senderDisplayName: "", date: date as Date!, media: mediaItem) {
self.messages.append(message)
if (mediaItem.image == nil) {
self.photoMessageMap[snapshot.key] = mediaItem
}
self.collectionView.reloadData()
}
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 = downloadRef?.observe(.childChanged, with: { (snapshot) in
let key = snapshot.key
let messageData = snapshot.value as! Dictionary<String, String> // 1
if let photoURL = messageData["imageUrl"] as String! { // 2
// The photo has been updated.
if let mediaItem = self.photoMessageMap[key] { // 3
self.fetchImageDataAtURL(photoURL, forMediaItem: mediaItem, clearsPhotoMessageMapOnSuccessForKey: key) // 4
}
}
})
}
It's very likely the error is a result of force casting - as!
Instead of
let messageData = snapshot.value as! Dictionary<String, String>
do
guard let messageData = snapshot.value as? Dictionary<String, String> else { return }
Your snapshot.value is either nil, or is not an instance of Dictionary<String, String>, and force casting it to such will result in crash.
You should also read more about optionals and type casting in Swift, because you use ! a lot, and not once in your program is it used correctly.
I've set the user notification. They got deliver well but the badge on the app icon is always one.
Here's my code:
let center = UNUserNotificationCenter.current()
let options: UNAuthorizationOptions = [.alert, .badge, .sound];
center.requestAuthorization(options: options) {
(granted, error) in
if !granted {
// ask for permission
}
}
When the use click the button i schedule the notif:
#IBAction func saveBtnPressed(sender: UIButton) {
scheduleNotif()
}
Here's the scheduleNotif function
func scheduleNotif() {
let dateformatter = DateFormatter()
dateformatter.dateStyle = DateFormatter.Style.medium
dateformatter.timeStyle = DateFormatter.Style.short
let dateFromString = dateformatter.date(from: selectDateTextField.text!)
let fireDateOfNotification: Date = dateFromString!
//Notif are enabled
let content = UNMutableNotificationContent()
content.title = notifTitleTextField.text!
content.body = notifNoteTextView.text
content.sound = UNNotificationSound.default()
content.badge = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber
var trigger: UNCalendarNotificationTrigger
var triggerDate = DateComponents()
trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
let titleNospace = notifTitleTextField.text?.replacingOccurrences(of: " ", with: "")
var identifier = titleNospace
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
self.center.add(request, withCompletionHandler: { (error) in
if let error = error {
print(error.localizedDescription)
}
})
}
Any idea why the badge will always display one and never increment as the notifications are delivered?
Thank you
I think you need to increment applicationIconBadgeNumber. So replace
content.badge = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber
By:
UIApplication.shared.applicationIconBadgeNumber += 1
content.badge = UIApplication.shared.applicationIconBadgeNumber as NSNumber