Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

detect unacknowledged UILocalNotifications

It seems that

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

and

didReceiveLocalNotification:(UILocalNotification *)notification

are only triggered if the user acknowledges the UILocalNotification, for example by swiping the slider or touching the entry in iOS's Notification pull-down.

Is there any way to tell that a UILocalNotification has gone off if the user ignores the UILocalNotification and re-enters the app by simply clicking on the app icon?

I should mention that this really only applies to repeating notifications because the firing of non-repeating notifications can be detected by observing the total count. That is, when they fire, the vanish from [[UIApplication sharedApplication] scheduledLocalNotifications].

I'm looking for something like..

[[UIApplication sharedApplication] unacknowledgedLocalNotifications]

Alas, I can't find anything like it.

like image 723
ari gold Avatar asked May 09 '13 19:05

ari gold


2 Answers

Well, you can check your scheduled notifications inside [[UIApplication sharedApplication] scheduledLocalNotifications]. To find out if a scheduled repeating notification has fired access the fireDate property to see what was the initial date set for the notification. Then check the repeatInterval property.

So there you have 2 variables, one is the initial NSDate, lets say 2013-05-08 12:00 and second is the repeat interval, lets say daily. And by doing a [NSDate date] you will get the current date which where I'm located (in Sweden) is now 2013-05-09 22:45. So this means that there is one notification the user has not acted on.

So you will need to create a method that will take these arguments and then iterate from the initial date to see how many notifications that have been missed until the current datetime.

You will find NSCalendars dateByAddingComponents:toDate:options useful.

like image 167
Peter Warbo Avatar answered Oct 04 '22 15:10

Peter Warbo


Everyone has likely since moved on, but I'd like to share my solution to this problem. (Sorry about the long variables names...)

The idea is simple: always keep the fireDate in the future.

-every time didFinishLaunchingWithOptions or didReceiveLocalNotification is invoked, simply cancel your current notification and reschedule a new one with a fireDate one interval unit in the future

-When your app launches iterate through all scheduled notifications, if the fireDate is not in the future you know that it was ignored

In my case, the notifications have a weekly repeat interval. I first reschedule any acknowledged notifications in didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UILocalNotification* localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];

    if (localNotif != nil)
    {
        [NotificationsHelper rescheduleNotification:localNotif];
    }
}

And also in didReceiveLocalNotification:

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *) notification
{
   [NotificationsHelper rescheduleNotification:notification];
}  

At App Launch I check all notifications for any with a fireDate in the past:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [self checkLocalNotifications:application];
}

Code for my "checkLocalNotifications" function:

- (void) checkLocalNotifications:(UIApplication *) application
{
    UIApplication*  app        = [UIApplication sharedApplication];
    NSArray*        eventArray = [app scheduledLocalNotifications];

    for (int i = 0; i < [eventArray count]; i++)
    {
        UILocalNotification* notification = [eventArray objectAtIndex:i];

        if ([NotificationsHelper wasWeeklyRepeatingNotificationIgnored:notification])
        {
            [NotificationsHelper rescheduleNotification:notification];

            NSLog(@"NotificationWasIgnored: %@ %@",notification.alertAction, notification.alertBody );
        }
    }
}

Code for my "wasWeeklyRepeatingNotificationIgnored" function:

+ (BOOL) wasWeeklyRepeatingNotificationIgnored:(UILocalNotification*) the_notification
{
    BOOL    result;
    NSDate* now = [NSDate date];

    // FireDate is earlier than now
    if ([the_notification.fireDate compare:now] == NSOrderedAscending)
    {
        result = TRUE;
    }
    else
    {
        result = FALSE;
    }

    return result;
}

Code for my "rescheduleNotification" function:

+ (void) rescheduleNotification:(UILocalNotification*) the_notification
{
    UILocalNotification* new_notification = [[UILocalNotification alloc] init];

    NSMutableDictionary* userinfo = [[NSMutableDictionary alloc] init];

    [new_notification setUserInfo:userinfo];
    [new_notification setRepeatInterval:the_notification.repeatInterval];
    [new_notification setSoundName:UILocalNotificationDefaultSoundName];
    [new_notification setTimeZone:[NSTimeZone defaultTimeZone]];
    [new_notification setAlertAction:the_notification.alertAction];
    [new_notification setAlertBody:the_notification.alertBody];
    [new_notification setRepeatCalendar:[NSCalendar currentCalendar]];
    [new_notification setApplicationIconBadgeNumber:the_notification.applicationIconBadgeNumber];

    NSCalendar*       gregorian         = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents* weekdayComponents = [gregorian components:NSWeekdayCalendarUnit
                                                       fromDate:the_notification.fireDate];

    NSInteger weekday   = [weekdayComponents weekday];
    NSDate*   next_week = [self addDay:weekday toHourMinute:the_notification.fireDate];

    [new_notification setFireDate:next_week];

    [[UIApplication sharedApplication] scheduleLocalNotification:new_notification];
    [[UIApplication sharedApplication] cancelLocalNotification:the_notification];
}
like image 33
dtmland Avatar answered Oct 04 '22 16:10

dtmland