I ended up doing it like TawaNicolas suggested in his answer, by getting the received notifications with getDeliveredNoti....
, then check the userInfo of every notification to find which ones I wanted to delete. I stored the removable notifications' identifiers in an array, and called removeDelivered...
.
This is exactly as his answer suggests, but this didn't work at first, and I had a hard time finding out why. I'm still not completely sure I have fixed it, but my tests shows that it's working - and my solution somewhat makes sense.
The thing was, inside the didReceiveRemote..
-function, you have to call completionHandler(.newData)
at the end. This is to notify the NotificationCenter that something has changed. I started to suspect that this callback was called before the removable notifications actually got removed. I checked the documentation, and removeDeliveredNotifications
is indeed async. This means that when I do this:
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removableIDs)
completionHandler(.newData)
it is not guaranteed that the first function is completed before calling the second function. This means that the completionHandler
, which tells my NotificationCenter that something has been updated, is completed first, and THEN the notifications gets removed from the system. (NotificationCenter apparently does not invoke its own completionHandler to update the UI after that function).
All this may or may not happen. As I was experiencing; when connected to the debugger, the removeDeliveredNotifications
-function was so fast that it was always completed before the completionHandler
was invoked, meaning that the notifications were removed before updating the system. So everything looked great when developing this. When I disconnected from the debugger, the removeDeliveredNotifications
-function was slightly slower, and since it's async, it was completed after I invoked the completionHandler
- causing the system to update too soon.
The best way to solve this would be for Apple to give us a completionBlock for removeDeliveredNotifications
and call our completionHandler
inside it, but they haven't.
To solve this now I have gone dirty by adding a fixed delay of 0.2 seconds. It could probably be lower than 0.2, but it isn't really important with a second from or to for what we're doing.
This is a class and function I created to easily delay something from anywhere:
class RuntimeUtils{
class func delay(seconds delay:Double, closure:@escaping ()->()){
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay*Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
}
And here I use it inside didReceiveRemoteNotification:
in AppDelegate
:
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: removableIDs)
RuntimeUtils.delay(seconds: 0.2, closure: {
completionHandler(.newData)
})
After adding this delay to the completionHandler, it is always executed after my deletion of notification, and it seems to work every time, with or without debugger connected.
This was a disgusting problem, with a nasty fix.