Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clear a remote pushed notification for iOS?

So I've been reading up on remote notifications, and have finally made it work; our app is receiving notifications from our server. We now want to remove or update an unread notification if a certain condition meets on our server (e.g the notification is no longer valid). I understand that "silent" notifications are the only way to go, but I am still confused as to how. If a silent notification triggers my app to wake up, I would be able to schedule local notifications, but will I be able to remove already existing remote notifications?

Is the only solution to exclusively use silent notifications from the server, and schedule all notifications as local notifications with a custom identifier which I can later remove? E.g, can I never use fire&forget remote push notifications from my server to devices if I want this feature?

Edit: This app supports down to iOS 9 :/

like image 607
Sti Avatar asked Mar 07 '23 20:03

Sti


1 Answers

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.

like image 78
Sti Avatar answered Mar 16 '23 11:03

Sti