iPhone: Incrementing the application badge through a local notification

Stefan picture Stefan · May 11, 2011 · Viewed 23k times · Source

is it possible to increment the application badge through a local notification while the app is not running?

I know how to set the badge, but haven't found any way to increment this value.

localNotification.applicationIconBadgeNumber = 23;

Update: I found a (far from being perfect) solution. You can predict what will happen, if the user doesn't open the app and add notifications for every +1 event.

An example:

  • For day 1: Count = 0
  • For day 2: localNotification.applicationIconBadgeNumber = 1;
  • For day 3: localNotification.applicationIconBadgeNumber = 2;
  • For day 4: localNotification.applicationIconBadgeNumber = 3;

==> Put these notifications in an array and set them before the application exits.

However, I'm searching for a better solution than this workaround.

Answer

Ronny Webers picture Ronny Webers · Mar 17, 2013

I've found, implemented & tested a 'workaround' for (apparantly) auto-incrementing the app icon's badge number, that works fine with non-repeating local notifications

It is indeed not possible for UILocalNotifications to have iOS 'automatically' update/increment the badge number when multiple local notifications are fired, and the user 'ignores' them or doesn't handle them immediately, so they 'pile up' in the Notification centre.

Also 'adding some callback method' to your app cannot take care of the 'auto increment', because the whole notification thing is handled 'outside' of your app by iOS, your app doesn't even need to be running.

However there is some workaround, that is based on the knowledge which I found through experimenting, because the XCode documentation is too vague on the badge property.

  • the badge is just an 'integer', actually more like a 'dummy label' that you assign to the applicationIconBadgeNumber property, right before you register the notification. You can give it any value - when the notification fires, iOS will add that value to the badge, whatever you set it to at the time you registered the notification. There is no magic 'auto-increment' or other manipulation by iOS (maybe that is different with push notifications, but that's not the subject here). iOS just takes the number (integer) from the registered notification, and puts it in the badge.

So for a 'workaround' your app must already provide the correct, incrementing badge number for each notification it newly creates and registers 'on top of the pending notifications'.

Since your app cannot look in the future, and know which events you'll handle immediately, and which ones you'll leave 'pending' for a while, there's some trick to do :

When notifications are handled by your app (by tapping on the notification(s), icon, ...), you have to :

  1. get a copy of all pending notifications
  2. 'renumber' the badge number of these pending notifications
  3. delete all pending notifications
  4. re-register the copy of the notifications with their corrected badge numbers again

Also, when your app registers a new notification, it has to check how many notifications are pending first, and register the new notification with with :

badgeNbr = nbrOfPendingNotifications + 1;

Looking at my code, it will get clearer. I tested this, and it's definitely working :

In your 'registerLocalNotification' method you should do this :

NSUInteger nextBadgeNumber = [[[UIApplication sharedApplication] scheduledLocalNotifications] count] + 1;
localNotification.applicationIconBadgeNumber = nextBadgeNumber;

When you handle the notification (appDelegate), you should call the method below, which clears the badge on the icon and renumbers the badges for pending notifications (if there are any)

Note that the next code works fine for 'sequential' registered events. If you would 'add' events in between pending ones, you'll have to 're-sort' these events first. I didn't go that far, but I think it's possible.

- (void)renumberBadgesOfPendingNotifications
{
    // clear the badge on the icon
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];

    // first get a copy of all pending notifications (unfortunately you cannot 'modify' a pending notification)
    NSArray *pendingNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];

    // if there are any pending notifications -> adjust their badge number
    if (pendingNotifications.count != 0)
    {
        // clear all pending notifications
        [[UIApplication sharedApplication] cancelAllLocalNotifications];

        // the for loop will 'restore' the pending notifications, but with corrected badge numbers
        // note : a more advanced method could 'sort' the notifications first !!!
        NSUInteger badgeNbr = 1;

        for (UILocalNotification *notification in pendingNotifications)
        {
            // modify the badgeNumber
            notification.applicationIconBadgeNumber = badgeNbr++;

            // schedule 'again'
            [[UIApplication sharedApplication] scheduleLocalNotification:notification];
        }
    }
}

To be truly 'bullet proof', this method should be 'atomic' (kernel) code, preventing the iOS from firing of a notification during the execution of this method. We'll have to take this risk here, chances are very small this will happen.

This is my first contribution to Stackoverflow, so you can comment also if I'm not following the 'rules' here