I want to clear remote notifications so they don't add up in the Notification Center (like when you get a video call in WhatsApp or Messenger, only the last notification is displayed).
I tried to call (in didReceiveRemoteNotification):
let center = UNUserNotificationCenter.current()
center.removeDeliveredNotifications(withIdentifiers: ["notification_identifier"])
But it gets called only if the app is active. How can I do this if the app is in another state?
Thanks for your help.
After some research and thanks to Paulw1's answer, I found out there are two ways of doing this:
Remote only
Notifications can be collapsed remotely, you only have to send the notification with apns-collapse-id as a request header. Please note that it's only supported in HTTP/2 though. More information here.
Silent remote + local notification
The other way consists in sending a silent remote notification, with this kind of payload:
{
"type": "notification_type",
"aps" : {
"content-available": 1
}
}
It will call didReceiveRemoteNotification even if the app's state is inactive or background. Then, I create a local notification request (needs using UserNotifications, available from iOS10) :
let content = UNMutableNotificationContent()
content.body = "Notification message"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.25, repeats: false)
let request = UNNotificationRequest(identifier: "identifierToUpdate", content: content, trigger: trigger)
self.center.add(request, withCompletionHandler: nil)
The key to update the previous notification is to use the same request identifier.
Related
I have an app for Apple Watch as a companion app for an iPhone app. The local notifications on Apple Watch App are delayed. I scheduled a local notification to be fired after 10 seconds. When I press the button on the watch that creates the notification and I exit the app, it doesn't take 10 seconds to display the notification but about 23 seconds.
I created a test stand alone/independent Apple Watch App with the same local notification code, and in this scenario, the local notification fires at the correct time.
I am on watchOS 8.1. Is this a new feature on Apple Watch or a bug? Because on iPhone it works without any delay. Thank you in advance :)
// Configure the notification's payload.
let content = UNMutableNotificationContent()
content.title = "Drink some milk!"
content.subtitle = "you have 10 sec"
content.sound = .default
content.categoryIdentifier = "myCategory"
let category = UNNotificationCategory(identifier: "myCategory", actions: [], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
let request = UNNotificationRequest(identifier: "milk", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error{
print(error.localizedDescription)
}else{
print("scheduled successfully")
}
}
There is a feedback here:
https://developer.apple.com/forums/thread/696280
This is correct behaviour.
If you have both a phone and watch app installed we’ll try to coordinate between phone and watch notifications if the phone is unlocked. If the app developer doesn’t send a notification on the phone the watch will timeout after 13 seconds and alert anyway.
The best solution to this would be to send the same notification on both devices ensuring the UNNotificationRequest.identifier matches on a per-instance basis.
This will let us alert and deduce correctly.
If watch app send local notification , I'll send the notification 13 seconds ahead of time. This way the notification appears to be on time
Hope to have a better solution in the future
We have an application that has CallKit feature. One problem I am facing is that if the user sets Device Do not Disturb mode on, then CallKit incoming notification is not shown if the device is locked.
There is a CallKit error that is CXErrorCodeIncomingCallErrorFilteredByDoNotDisturb when the device is in this mode, but I want to still show the notification to the user if a call arrives.
Note: I've found that WhatsApp still shows the incoming CallKit notification even when DND is enabled. Any help/suggestion will be appreciated.
If you want to display a notification when you receive a CXErrorCodeIncomingCallErrorFilteredByDoNotDisturb error, you could do the following:
cxProvider.reportNewIncomingCall(
with: aCallId,
update: vCallUpdate,
completion: { error in
guard let vError = error as? CXErrorCodeIncomingCallError else { return }
if vError.code == .filteredByDoNotDisturb {
let content = UNMutableNotificationContent()
content.title = "Call"
// ...
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request) { error in
if let vError = error {
print(vError.localizedDescription)
}
}
}
})
When do-not-disturb is active, WhatsApp doesn't display a notification after receiving an audio call, it only displays notifications for video calls. That's because they use CallKit and PushKit only for audio calls. For video calls they use normal push notifications.
I have multiple location notifications registered, often they overlap with each other. I want to limit to one fired notification per day, but the problem is that I do not have any way to cancel all the pending notifications when app is killed and can only do it when BGAppRefreshTaskRequest is triggered (which is not a common thing most of the times). And the worst part is that if user is in overlapping area he'll receive multiple push notifications at once.
Code to register notification:
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "Body"
content.sound = .default
content.userInfo = ["locationNotificationId" : id]
content.categoryIdentifier = "geofenceNotifications"
let region = CLCircularRegion(center: location,
radius: CLLocationDistance(range),
identifier: id)
region.notifyOnEntry = true
region.notifyOnExit = false
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
let request = UNNotificationRequest(identifier: id,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if let error = error {
print(error)
}
})
What do I do to prevent other notifications being triggered after one is fired if app is in the background or killed?
Ok, so I managed to resolve the issue by monitoring region itself instead of using local push notification with UNLocationNotificationTrigger. The downsides are - I need to ask location access for background instead of "when in use" and I'm limited to 20 regions but it still better than uncontrolled flow of push notifications.
To read more about region monitoring: https://www.raywenderlich.com/5470-geofencing-with-core-location-getting-started#toc-anchor-004
I have an app which uses UNLocationNotificationTrigger to deliver notification based on user's location. When I'm testing my app sometimes it registers that user entered specific region, but doesn't deliver notification on entering region. Other times it works perfectly but I was wondering why this doesn't work every time. Does anyone else have similar problem? I would appreciate if someone could give a short explanation on this problem :)
This is the piece of code for registering notification:
let notification = UNMutableNotificationContent()
notification.title = "Notification title"
notification.subtitle = "Notification subtitle"
notification.body = "Notification body"
let notificationTrigger = UNLocationNotificationTrigger(region: enteredRegion, repeats: true)
let notificationRequest = UNNotificationRequest(identifier: "id12494", content: notification, trigger: notificationTrigger)
UNUserNotificationCenter.current().add(notificationRequest, withCompletionHandler: nil)
In our app, the user will clock-in when he starts his work. If the user forgets to clock-out, we will have to automatically clock him out after 24 hours from the clock-in time. The app might not be in the active/background state for such a long time. It might be terminated. So our idea is to post a local notification through which will execute the code in the background to clock him out. This notification has to be a silent notification. But our understanding from the research is that local notifications cannot be silent. So is there any other way we could achieve this? Or can we actually schedule a silent local notification?
class func generateLocalNotificationWith(timeInterval : TimeInterval, title : String, message : String, mobileTimeClockId: Int)
{
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
let content = UNMutableNotificationContent()
//adding title, subtitle, body and badge
content.title = title
content.body = message
let userInfoDictionary = ["badge":"0","content-available": "1"]
let dict = ["aps": userInfoDictionary, "MobileTimeClockId": mobileTimeClockId] as [String : Any]
content.userInfo = dict
//getting the notification trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
//getting the notification request
let request = UNNotificationRequest(identifier: "SimplifiedIOSNotification", content: content, trigger: trigger)
//adding the notification to notification center
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
If you want a notification to be silent and do background work it has to be a push notification. A local notification can't wake your app without some user interaction.
The best way to do what you want is to use Core Location region monitoring to wake your app when the user physically leaves their workplace. Region monitoring can wake your app silently in the background and let you do some work. The problem is that it's not 100% assured that it will be triggered and the user will need to accept background location permissions.