Paulo Almeida
Paulo Almeida

Reputation: 2154

Find out last fired UILocalNotification

I'm working on an Alarm app for iOS which uses UILocalNotifications. So far so good, until somebody came up with the idea of showing a route from where the user is to where he wants to go.

For this I thought I could maybe get the last fired UILocalNotification ( All of them have repeatInterval set as weekly or daily).

Here is where the whole trouble comes in, since the fireDate is the first time I scheduled them: This is my scenario:

        UILocalNotification* ln = [[UILocalNotification alloc] init];
        ln.fireDate = date; // For instance 07/19/2014 10:00:00 (Saturday)
        ln.repeatInterval = NSDayCalendarUnit;            
        [[UIApplication sharedApplication] scheduleLocalNotification:ln];

        UILocalNotification* ln = [[UILocalNotification alloc] init];
        ln.fireDate = date; // For instance 07/17/2014 11:00:00 (Thursday)
        ln.repeatInterval = NSWeekCalendarUnit;            
        [[UIApplication sharedApplication] scheduleLocalNotification:ln];

Let picture this:

Next Thursday (07/24/2014 11:00:01) I want to know that the last fired UILocalNotification was the second one that only repeats on Thursdays and the next day I want the first one since it repeats daily.

I've tried to sort by date all LocalNotifications with no success.

Has anyone ever faced a situation akin to it before?

Regards

Upvotes: 2

Views: 1115

Answers (3)

Paulo Almeida
Paulo Almeida

Reputation: 2154

Based on @Sid's answer and some code snippets from SO I could come up with this solution which calculates the last fired date for all local notifications without writing it to a file or even to a database. ( Although writing it somewhere isn't a bad idea )

Header

#import <UIKit/UIKit.h>

@interface UILocalNotification (LastFireDate)

- (NSDate *)lastFireDateBeforeDate:(NSDate *)afterDate;

@end

Implementation

#import "UILocalNotification+LastFireDate.h"

@implementation UILocalNotification (LastFireDate)

- (NSDate *)lastFireDateBeforeDate:(NSDate *)afterDate
{
    // Check if fire date is in the future:
    if ([afterDate compare:self.fireDate] == NSOrderedAscending)
        return [NSDate dateWithTimeIntervalSince1970:0];

    // The notification can have its own calendar, but the default is the current calendar:
    NSCalendar *cal = self.repeatCalendar;
    if (cal == nil)
        cal = [NSCalendar currentCalendar];

    // Number of repeat intervals between fire date and the reference date:
    NSDateComponents *difference = [cal components:self.repeatInterval
                                          fromDate:self.fireDate
                                            toDate:afterDate
                                           options:0];

    // Add this number of repeat intervals to the initial fire date:
    NSDate *lastFireDate = [cal dateByAddingComponents:difference
                                                toDate:self.fireDate
                                               options:0];

    // If necessary, subtract one interval:
    if ([lastFireDate compare:afterDate] == NSOrderedDescending) {
        switch (self.repeatInterval) {
            case NSWeekCalendarUnit:
                difference.week--;
                break;
            case NSDayCalendarUnit:
                difference.day--;
                break;
            case NSHourCalendarUnit:
                difference.hour--;
                break;
            default:
                break;
        }
        lastFireDate = [cal dateByAddingComponents:difference
                                            toDate:self.fireDate
                                           options:0];
    }
    return lastFireDate;
}


@end

Usage

    NSDate* date = [NSDate date];
    NSArray* arrayNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];

    if([arrayNotifications count])
    {
        NSArray *sorted = [arrayNotifications sortedArrayUsingComparator:^NSComparisonResult(UILocalNotification *obj1, UILocalNotification *obj2) {
            NSDate *next1 = [obj1 lastFireDateBeforeDate:date];
            NSDate *next2 = [obj2 lastFireDateBeforeDate:date];
            return [next1 compare:next2];
        }];

        UILocalNotification* lastLocalNotification = [sorted lastObject];
        NSLog(@"%s %@",__PRETTY_FUNCTION__,[lastLocalNotification lastFireDateBeforeDate:date]);
    }

Hope it helps someone who is facing the same issue

Cheers

Upvotes: 2

Sid
Sid

Reputation: 1144

iOS will discard a local notification after it fires even if it is scheduled to repeat itself via the repeatInterval property.

[UIApplication sharedApplication].scheduledLocalNotifications actually returns all notifications that are scheduled to be fired in the future. So their fireDate will be set to a date in the future.

After further investigation, it seems that the fireDate is always set to the original value specified when the notification was created. However, the next fire date, a calculated value, will be in the future. To see this value simply run po yourNotificationObj in the debugger and you will see the following type of output, which includes the next fire date:

{fire date = Saturday, July 26, 2014 at 1:42:50 PM Eastern Daylight Time, time zone = (null), repeat interval = NSMinuteCalendarUnit, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Saturday, July 26, 2014 at 1:43:50 PM Eastern Daylight Time, user info = (null)}

How do you calculate the next fire date? Please see the following code taken from How to grab the NEXT fire date from a UILocalNotification object:

NSCalendar *calendar = localNotif.repeatCalendar;
if (!calendar) {
  calendar = [NSCalendar currentCalendar];
}

NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
// If you use some other repeat interval you would have to change the code accordingly. 
// If you were to use NSMonthCalendarUnit you would have 
// to use `components.month = 1' instead
components.day = 1;
NSDate *nextFireDate = [calendar dateByAddingComponents:components toDate:localnotif.fireDate options:0];

If you want to remember the last fired local notification then you must build that logic into your app and save the required information. For example, you could do the following:

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

    if (localNotification) {
        [self saveLocalNotification:localNotification];
    }

    return YES;
}

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

- (void)saveLocalNotification:(UILocalNotification *)notification
{
    // save information about the last fired local notification on disk
}

- (UILocalNotification *)lastFiredLocalNotification
{
    // read the information stored on disk by the saveLocalNotification: method
    // and recreate the notification. I'm just saying recreate the notification
    // because I don't know what exactly you're trying to do. You 
    // can return anything you want here
}

Using the above setup, you can call the lastFiredLocalNotification method whenever you need it.

Upvotes: 5

Aaron Brager
Aaron Brager

Reputation: 66234

iOS discards local notifications after they're fired. You're responsible for maintaining this information in some other way if you need the information later.

If you want, you can inspect [UIApplication sharedApplication].scheduledLocalNotifications.firstObject to see the next one that's scheduled, then use that information to determine from your own data model which was before it.

Upvotes: 2

Related Questions