Why are notifications not removed with removeDeliveredNotifications?
Asked Answered
T

1

5

Up until recently (I believe prior to iOS 12 release), removing remote push notifications from the Notification Center worked as expected using removeDeliveredNotifications.

Suddenly, without any code change in the Notification Service Extension, notifications are not removed anymore.

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {

    self.contentHandler = contentHandler
    self.content = request.content.mutableCopy() as? UNMutableNotificationContent

    guard let content = content else {
        contentHandler(request.content)
        return
    }

    UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
        let matchingNotifications = notifications.filter({ $0.request.content.threadIdentifier == "myThread" && $0.request.content.categoryIdentifier == "myCategory" })
        UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: matchingNotifications.map({ $0.request.identifier }))
        contentHandler(content)
    }
}

The function just completes without removing the notification. When debugging on a real device, it shows that matchingNotifications contains notifications and the notification IDs to remove are correctly provided.

For testing, calling removeAllDeliveredNotifications() works and removes all notifications.

The function above is called in override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)

What is the problem here?

Theretofore answered 9/12, 2018 at 22:22 Comment(8)
Hey @Manuel, did you find any solution to solve this problem ?Xray
@Xray No, it is still an issue. Please post here if you find a solution.Theretofore
Can you post your complete didReceive implementation? I believe it's related to when you call the contentHandler completion.Gorge
@Gorge Completion handler is called after removing the notifications, see updated code. Again, it always worked fine for months until suddenly it stopped working.Theretofore
From what I have read this behaviour indeed changed since iOS 12. The delete method returns immediately but executes asynchronously, calling the contentHandler unloads/destroys your extension (I assume) which may stop the asynchronous deletion. Can you try delay calling contentHandler for testing purposes? (delay for a couple of seconds for example)Gorge
@Gorge I will test this and post an update. Do you have a reference for this new behavior?Theretofore
@Theretofore It's not documented officially, just saw some people online experiencing the same behaviour since iOS 12. (a comment on this blogpost by the Guardian for example)Gorge
@Gorge have anyone founded the solution? It's only iOS 12+ problem. I'll much appreciatePyre
F
7

I tried the suggestion by @Kymer and verified that calling contentHandler after waiting some time (e.g. 3 seconds) resolved the issue for me, e.g.

// UNUserNotificationCenter *notificationCenter
// NSArray(NSString *) *matchingIdentifiers;
// UNNotificationContent *content;
if (matchingIdentifiers.count > 0) {
    NSLog(@"NotificationService: Matching notification identifiers to remove: %@.", matchingIdentifiers);               
    [notificationCenter removeDeliveredNotificationsWithIdentifiers:matchingIdentifiers];

    // Note: dispatch delay is in nanoseconds... :(
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3000000000), dispatch_get_main_queue(), ^{
        NSLog(@"Replacing content after 3 seconds.");
        self.contentHandler(content);
    });
}

So I think this means it's a timing issue, with iOS freezing the process aggressively after the contentHandler is invoked, and removing any pending removal requests in the notificationCenter

EDIT: Although the question wasn't about how to deal with it, the comment section brought concerns about an arbitrary time delay. In my testing, posting the callback on another loop sufficed, e.g.

dispatch_async(dispatch_get_main_queue(), ^{
    contentHandler(content);
});
Flank answered 18/6, 2019 at 22:37 Comment(4)
If an arbitrary delay is the only solution, then this must be a bug.Theretofore
This workaround worked. I did not try to find the minimum required delay time, but empirically it is enough to use 500ms as delay. Side note: the delay may have a visible effect to the user in the notification center, because notifications are removed immediately when calling removeDeliveredNotifications, but whatever code comes next is executed after the delay.Theretofore
BTW: you don't need to add an arbitrary dispatch delay. i verified it works by posting the callback to self.contentHandler(content) on another loop, e.g. the next poll on the main queue dispatch_async(dispatch_get_main_queue(), ^{ contentHandler(content); }); (sorry can't figure out code block in mini-Markdown)Flank
@SimonBocanegraThiel that never worked for me, I used a 0.1 delay instead.Cuffs

© 2022 - 2024 — McMap. All rights reserved.