iOS badge number live update iOS badge number live update xcode xcode

iOS badge number live update


It's not easy to do this currently in iOS. You can get close, but not update the badge reliably every day unless the user opens the app occasionally.

You'll need to use UILocalNotification. You can create and schedule a "silent" notification that updates the app badge without alerting the user or playing an alert sound like this:

UILocalNotification* notification = [[UILocalNotification alloc] init];notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:seconds];notification.timeZone = [NSTimeZone systemTimeZone];notification.alertBody = nil;notification.soundName = nil;notification.applicationIconBadgeNumber = dayTomorrow;[[UIApplication sharedApplication] scheduleLocalNotification:notification];[notification release];

where dayTomorrow is an int of tomorrow's day of the month. and seconds is some time interval - this could be something like the interval to midnight. When this notification is delivered, there will be no alert or sound for the user, but the app badge should be updated to the new value. You could make this notification repeat every day using the repeatInterval property, but the problem here though is that the next day you need to change dayTomorrow to be a different value, and again the day after that. But a repeated alert will always have the same value in applicationIconBadgeNumber.

So I think the only way to achieve anything close to what you want is to schedule multiple of those local notifications - you can schedule up to 64 in advance - each one a day apart, and each one with that day's value set as the applicationIconBadgeNumber. These need to be non-repeating, so make sure you set repeatInterval to nil (or don't set it, as nil is the default value). Assuming all of these notifications are delivered reliably by iOS, you would be able to update the badge number silently and without bothering the user for up to 64 days. After that, the badge would stop updating... unless you manage to schedule more notifications in the meantime - i.e. when the user opens up the app. What you could try doing is cancelling all existing scheduled notifications at app launch:

[[UIApplication sharedApplication] cancelAllLocalNotifications];

and then loop through 64 days in advance, scheduling a notification for midnight each day with the correct day of the month for the applicationIconBadgeNumber property. As long as the user opened up your app at least once every 64 days, you would keep the badge icon up to date. If you envisage your app users opening the app frequently (e.g. multiple times per day), then an optimisation might be to check the number of existing notifications scheduled before cancelling them and creating 64 new ones, like this:

if ([[[UIApplication sharedApplication] scheduledLocalNotifications] count] < 10){    //there are 10 or fewer days' worth of notifications scheduled, so create and    //schedule more here, up to 64 in total.}

A couple of other points to note:

  • I'm not sure on how reliable the local notifications are. Push notifications are not guaranteed, but I would hope and expect that local notifications are guaranteed to be delivered. e.g. if the phone is turned off at midnight and only turned on at say 7 AM, I would hope that the local notification scheduled for midnight would be immediately delivered. But I've never tested this.
  • If your app is currently open when a scheduled notification is delivered, the application:didReceiveLocalNotification: is called but the badge icon will not be updated. So in order to cater for this case, I would suggest updating the badge icon to the current day every time the app is launched (or returns to the foreground from the background).
  • Not sure what Apple would make of this use of the badge icon. On reviewing your app, they might feel that it is confusing to the user since it is normally used to indicate the number of items of new/changed data in an app. So your app could get rejected for this.
  • Ideally Apple would provide an API for developers to provide this kind of "at a glance" info to the user, whether that's achieved by changing the application icon/badge in some way, or by a widget etc. What I've described above should work but is clearly a bit of a kludge to get around the current limitations of iOS. The built in calendar app changes its icon every day to show the day of the month and I can see a lot of apps could benefit from this behaviour - e.g. the weather app could show an icon of the weather conditions at the current location.

EDIT:

Here's a code snippet to cancel any existing local notifications and schedule 64 in the future for midnight and with the correct day of the month specified as the badge number:

[[UIApplication sharedApplication] cancelAllLocalNotifications];NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];NSDateComponents* components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];[components setHour:0];[components setMinute:0];[components setSecond:0];NSDate* midnightToday = [calendar dateFromComponents:components];const int SECONDS_PER_DAY = 60 * 60 * 24;for (int i = 1; i <= 64; i++){    NSDate* futureDate = [midnightToday dateByAddingTimeInterval:i * SECONDS_PER_DAY];    NSDateComponents* components = [calendar components:NSDayCalendarUnit fromDate:futureDate];    UILocalNotification* notification = [[UILocalNotification alloc] init];    notification.fireDate = futureDate;    notification.timeZone = [NSTimeZone systemTimeZone];    notification.alertBody = nil;    notification.soundName = nil;    notification.applicationIconBadgeNumber = components.day;    //NSLog(@"futureDate: %@", [NSDateFormatter localizedStringFromDate:futureDate dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterMediumStyle]);    //NSLog(@"notification: %@", notification);            [[UIApplication sharedApplication] scheduleLocalNotification:notification];    [notification release];}[calendar release];

What's going on here is:

  • We take today's date with [NSDate date], and then break it into the components we want to keep (year, month, day), and then set the time components (hour, minute, second) to be midnight. This gives us midnightToday.
  • We loop through 64 times, each time creating a new date that is from 1 to 64 days in the future by taking midnightToday and adding on the number of seconds per day multiplied by how many days in the future we want the date to be. We use this date to schedule the local notification.
  • We also need to work out the day of the month for the badge number. So again we use NSDateComponents to break the future date into the components we are interested in - which in this case is only the day so we specify NSDayCalendarUnit. We can then set the applicationIconBadgeNumber to be components.day
  • Each of the 64 notifications is scheduled with UIApplication, to be delivered 1 to 64 days in the future.

Note that this will probably only work for users that use the Gregorian (western style) calendar - depending on your market you might need to consider the other calendar types in use around the world and support those too.


You should be able to do it with UILocalNotification

I'm not sure if you can set it to auto repeat once a day but you might be able to spam noticifations to emulate that.

http://developer.apple.com/library/IOs/#documentation/iPhone/Reference/UILocalNotification_Class/Reference/Reference.html


How and where are you calling this method? If you set a timer to run this code every x seconds it will update on the next timer fire.