iosusernotifications

iOS: all local notifications disappear instead of the ones I list


My app uses a lot of scheduled local notifications and on certain events I reschedule the notifications and want to clear some of the delivered notifications, not all of them.

Rough pseudo code:

// Clear pending notifications that haven't been delivered yet
notificationCenter.removeAllPendingNotificationRequests()

// Get the delivered notifications (async), filter out the ones that should be removed
// and remove them
notificationCenter.getDeliveredNotifications() { notifications in
  let notificationsToRemove = notifications.filter { some boolean operation }
  let identifiersToRemove = notificationsToRemove.map { $0.identifier }
  notificationCenter.removeDeliveredNotificationsWithIdentifiers(identifiersToRemove)
}

// Schedule the next set of notifications
let nextBatchOfNotifications = notificationGenerator.generate()
for notification in nextBatchOfNotifications) {
  notificationCenter.schedule(notification)
}

But when I do this the vast majority of the time results in all delivered notifications being cleared. And in very rare cases it results in only some of the delivered notifications I ask to be removed being removed (or maybe none).


Solution

  • At least in my case it transpires that the async nature of all the functions related to querying and scheduling notifications was the problem and the fact removing pending/delivered notifications and trying to schedule new ones meant that iOS would get in a bit of a mess and not do what I asked properly.

    The solution appears to be to wait for the delivered notifications to be returned, remove them, wait a little more and then schedule the new ones. Since I've made this change I've not seen any issues so far!

    Rough pseudo code updated with the waits

    // Clear pending notifications that haven't been delivered yet
    notificationCenter.removeAllPendingNotificationRequests()
    
    // Get the delivered notifications (async), filter out the ones that should be removed
    var identifiersToRemove
    var semaphore
    notificationCenter.getDeliveredNotifications() { notifications in
      let notificationsToRemove = notifications.filter { some boolean operation }
      identifiersToRemove = notificationsToRemove.map { $0.identifier }
      semaphore.signal()
    }
    
    semaphore.wait()
    notificationCenter.removeDeliveredNotificationsWithIdentifiers(identifiersToRemove)
    
    Thread.sleep(0.1)
    
    // Schedule the next set of notifications
    let nextBatchOfNotifications = notificationGenerator.generate()
    for notification in nextBatchOfNotifications) {
      notificationCenter.schedule(notification)
    }
    

    I'm not sure if there's a better way of doing the 0.1s wait after requesting for the delivered notifications to be removed or not... there's no callback to let me know that it's been done so it's the best I could come up with for now!

    (apologies for the pseudo code if it's difficult to follow, my code is somewhat legacy so is still in Objective-C and I didn't think that was particularly appropriate to share in this day and age!)