20

The iOS docs say that UNUserNotificationCenter's removeAllPendingNotificationRequests() is asynchronous.

What I want to do is this:

  1. Call removeAllPendingNotificationRequests() to get rid of all my scheduled notifications

  2. Schedule a bunch of new notifications, some of which may or may not have the same IDs as what was there previously

But since the documentation says that the method is asynchronously running on another thread (and there is no completion callback parameter) I'm worried that sometimes, depending on the vagaries of threads and timing and whatnot, that step #1 will still be going as I am creating things in step 2 and therefore it will also kill some of the new notifications I'm making.

This kind of stuff is a little tricky to test manually, since it depends on timing. So I'm curious is anyone knows if this is something I should be worried about or not...

andrewbuilder
  • 3,629
  • 2
  • 24
  • 46
Erik Westwig
  • 713
  • 4
  • 16
  • I need this answered too. Can't believe no one has given info on it. I have the same worry! – Jann Sep 27 '17 at 20:14
  • 1
    One would hope that the center itself would lock/synchronize its list, and that's why `add(_:withCompletionHandler:)` _does_ have a completion handler. But if that's the case, it should definitely be documented. – jscs Oct 30 '17 at 15:13
  • If the call were simply synchronous, then this would be a non-issue. – paiego Jul 13 '20 at 17:38

2 Answers2

10

In documentation for add notification I found this:

Calling -addNotificationRequest: will replace an existing notification request with the same identifier.

Maybe the solution would be something like this:

  1. Create new notification requests
  2. Get all pending and filter out only the ones that will not be replaced
  3. Delete not replaced
  4. Add all new notifications
let center = UNUserNotificationCenter.current()
// Create new requests
let newRequests: [UNNotificationRequest] = [...]
let identifiersForNew: [String] = newRequests.map { $0.identifier }

center.getPendingNotificationRequests { pendingRequests in
   // Get all pending notification requests and filter only the ones that will not be replaced
    let toDelete = pendingRequests.filter { !identifiersForNew.contains($0.identifier) }
    let identifiersToDelete = toDelete.map { $0.identifier }

    // Delete notifications that will not be replaced
    center.removePendingNotificationRequests(withIdentifiers: identifiersToDelete)

     // Add all new requests
     for request in newRequests {
       center.add(request, withCompletionHandler: nil)
     }
}
Nikola Ristic
  • 419
  • 4
  • 9
0

I have the same case as you and up to know I don't have a problem with this code:

 center.getPendingNotificationRequests(completionHandler: { notifications in
            var notificationIds:[String] = []
            for notification in notifications {
                if notification.identifier != "something_taht_I_dont_dismiss"{
                    notificationIds.append(notification.identifier)
                }
            }
            self.center.removePendingNotificationRequests(withIdentifiers: notificationIds)
            createAllNewNotifications()
        })

If you want to double check all if the pending notifications are removed you can create simple recursion method for checking.

    func removeAllNotificationsSafe() {
        center.removeAllPendingNotificationRequests()
        checkNotificationsAreRemoved()
    }

    func checkNotificationsAreRemoved() {
        center.getPendingNotificationRequests(completionHandler: { notifications in
            if notifications.count > 0 {
                self.checkNotificationsAreRemoved()
            } else {
                self.doWhathverYouWant()
            }
        }
    }

I don't believe this is needed, because all the actions of UNUserNotificationCenter will be synchronized between each other.

m1sh0
  • 2,236
  • 1
  • 16
  • 21