Find out when a repeating UILocalNotification was fired - ios

I have a UILocalNotification that is set to start at 2012-06-18 10:00 with a repeat interval of 1 minute.
At 2012-06-18 10:05 5 notifications would have been triggered. The user would then choose notification number 3. The notification should have been fired at 2012-06-18 10:02.
In my - application:didReceiveLocalNotification: method. How can I programatically get this fire date from notification number 3 which should be 2012-06-18 10:02. I know I can get the intitial/start fireDate property from the UILocalNotification but I'm not interested in that. I'm interested to know the fireDate of this repeating notification (not the intial/start fireDate).
If someone can explain to me how to find out which repeating UILocalNotification was fired without parsing the - description of the notification I will give you some of my hard earned bounty.

So I have given this a bit more thought, and as I said in my comment it doesnt seem possible because of the way notifications are handled.
You create one and fire immediately or schedule it.
A notification is just the storage of some information which means it is only meant to be read for its properties.
The application receives the notification through
application:didReceiveLocalNotification:
In where only the UILocalNotification itself is passed. This notification ONLY has a bunch of properties which were set at the start.
The repeat interval is only used to re-notify the user, it doesnt change what the notification has inside.
This comes to the conclusion that for the behavior you expect to achieve you would have to fire different notifications if you expect to pass different information or perform different actions.
(Sub-classing is also not useful as explained here https://stackoverflow.com/a/8583329/1068522)

The best alternate solution to getting the fireDate of the UILocalNotification is to calculate the date.
Given that you have an initial fireDate, you can use the repeated interval to calculate the fireDate of a given notification.
1.Start with the initial fire date
2.Get the notification number/index of the notification you have
3.Multiply the repeated interval by the index and add it onto your initial fire date
However, date calculating, as mentioned in the below link, is tricky due to time zones and "other nasty things."
Here's a helpful link:
How to grab the NEXT fire date from a UILocalNotification object
And of course, there is the final, fall back solution of parsing the description method. Using [notification.fireDate description] However, as you probably know, it is NEVER a good idea to do so because the formatting may change in the future, thus breaking your code.
Hope this helped!
Edit:
Example: Okay, so say my first initial fireDate was 2012-06-18 10:00
I know that my repeated interval is every ONE minute, right?
So say, that the user taps uilocalnotification number 3, then that means TWO minutes should have passed!
THEREFORE, the time for that specific notification is: 2012-06-18 10:02
Does this make sense?

Q:
If someone can explain to me how to find out which repeating
UILocalNotification was fired without parsing the - description of the
notification I will give you some of my hard earned bounty.
A: The easiest way:
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSDateComponents *comps = [[NSCalendar currentCalendar] components:notification.repeatInterval fromDate:notification.fireDate toDate:[NSDate date] options:0];
NSLog(#"Notification #%d", [comps minute] + 1);
}
Q: How to get that "next fire date" from description without parsing?
A: There is private/undocumented function for this: nextFireDateForLastFireDate
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"next: %#", [notification nextFireDateForLastFireDate:[NSDate date]]);
}
Q: Maybe there is some kind of count in notifications? property or method?
A: There's undocumented properties:
#property (assign, nonatomic) int totalRepeatCount;
#property (assign, nonatomic) int remainingRepeatCount;
But they seem to have always same value: UILocalNotificationInfiniteRepeatCount. Both of them is managed by operating system, so overwriting of this properties or underlying instance variable does nothing. Moreover, nothing changes inside UILocalNotifications from firing to firing, there's no way to distinguish one UILocalNotification from another (except address in memory). Next fireDate is calculated basing on current time.

Related

IOS/Objective-C: Is there a way to keep track of notifications before and after they are fired?

I gather that IOS does not give you access to the notifications center in order to keep track of notitifications.
However, many apps do seem to keep track of notifications so that when you see the badge on your home screen or a notification fires and you tap inside the app you can view all the notifications. Linkedin does this for example.
In my case, all the notifications refer to managedobjects in one entity in core data so I tried to hack this by estimating which notifications should have fired given their timestamps in core data. However, this is proving to be an unreliable approach. A fetch that grabbed every item beginning with the first notification since last opened would have to know the time stamp of the first notification since last opened. However, while you know the current time, you don't really know the time of the first notification if you don't know what the first notification since the last opening of the app is.
Has anyone devised a way or know of a library to accurately track notifications? It occurred to me one way might be create an accessible doppelganger of the notificationcenter in core data or an array and keep the notifications there. But that's as far as I've gotten.
To create the cache of future notifications in the notification center, I just fetch anything with a date in the future from core data and add the notification to the notification center:
NSDate *now [NSDate* date];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"event!=nil AND event.length>=1&&starttime>=now"];
//perform fetch code here
NSInteger resultCount = [fetchedObjects count];
UILocalNotification *reminderNotification;
for (int i = 0; i < resultCount; i++) {
reminderNotification = [[UILocalNotification alloc] init];
reminderNotification.fireDate = event.starttime;
reminderNotification.timeZone = [NSTimeZone defaultTimeZone];
reminderNotification.alertAction = #"View Event";
reminderNotification.alertBody = event.event;
reminderNotification.applicationIconBadgeNumber = resultCount+1;
//schedule the notification!
[[UIApplication sharedApplication]
scheduleLocalNotification:reminderNotification];
resultCount++;
}
Thanks in advance for any suggestions.

Update iOS icon badge number

I have a icon badge number update requirement. The app tracks tasks. I want the app to have a badge displaying the number of tasks due on each day. There are basically two cases when the badge number needs to be updated:
Midnight every day.
If new tasks are added or tasks are removed.
I know how to handle the second case. I can set badge number in the applicationResignActive func. However, the midnight automatic update is trick for me. To update the badge number, I need to call a func of the app to count the tasks that due on the day. However, in midnight, the app may be in all possible situations: foreground, background and not running. How can I do this? Thank you.
=====================================
To be clearer with my requirement, I would like the badge number to be updated everyday correctly, even the user never opens the app for a whole day or for consecutive several days. Also, I would try to avoid server side support because the app is a standalone app so far. Much appreciated for any help.
=====================================
Final update: I accepted Vitaliy's answer. However, his answer requires the app to be opened at least once every day. Otherwise, the event won't fire and the badge number cannot be updated.
Also, in my case, every time the app enters background event fires, I have to remove the existing notification and schedule a new one, with the up-to-dated badge number recalculated.
I am still interested in some way to handle the case that the app is not opened every day, how can you make sure the badge number is correct. So far, the easiest way is to setup some server and let it push notifications to the app regularly.
You can achieve it with UILocalNotification:
When app goes to background, calculate exact badge count number for nearest midnight
Schedule UILocalNotification at the nearest midnight with your calculated badge count
You will get notification at midnight, and app's badge count will be updated
Example code:
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Calculate nearest midnight or any other date, which you need
NSDate *nearestMidnight = [self nearestMidnight];
// Create and setup local notification
UILocalNotification *notification = [UILocalNotification new];
notification.alertTitle = #"Some title";
notification.alertBody = #"Some message";
notification.fireDate = nearestMidnight;
// Optional set repeat interval, if user didn't launch the app after nearest midnight
notification.repeatInterval = NSCalendarUnitDay;
// Calculate badge count and set it to notification
notification.applicationIconBadgeNumber = [self calculateBadgeCountForDate:nearestMidnight];
[application scheduleLocalNotification:notification];
}

How can I Specify Relative Time in UILocalNotification Alert Body (i.e. "starting in X minutes")?

I'm creating a local notification like this:
NSDate *fireDate = // start date from model
NSString *startingInMinutes = #"5 minutes"; // also actually from model
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = [NSString stringWithFormat:#"Your event is starting in %#", startingInMinutes];
notification.fireDate = fireData;
notification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
After the notification is shown to the user, however, the "5 minutes" string is quickly outdated.
For example, if the user doesn't interact with the notification for 30 minutes, it still shows "Your event is starting in 5 minutes." This is obviously due to the fact that the "5 minutes" is a hard-coded string.
Is there any syntax to specify a relative time (i.e. "in X minutes") in the alert body of a UILocalNotification?
You can't do anything about that.
It's a static text.
See documentation
This isn't possible because the notification's payload is set at the time it is scheduled (UILocationNotification) or delivered to APNS (remove notification).
You have two choices:
Leave your message as is and hope that the user realizes that the content of the message reflects a frozen point in time and can mentally do the math using the notification's timestamp.
Adjust the text to be slightly ambiguous ("Your event is starting soon") or reference an absolute time ("Your event starts at 4:00pm").
WatchKit does include WKInterfaceTimer, which is an OS-controlled counter label. It was created to decrease the amount of communication needed between an Apple Watch and an iPhone just to update UI as the time changes. If this functionality is important to you in a notification context, I would encourage you to submit a bug report asking for the enhancement at https://bugreport.apple.com.

Old Local Notifications Firing on iOS

I have a set of UILocalNotifications that I am scheduling. They are firing on time and seem to be working fine. However, on occasion, when a NEW notification fires there are multiple (sometimes only one) OLD notifications that fire along with it.
For example, I schedule a UILocalNotification to take out the trash on Monday at 5pm with no repeat interval. It fires no problem and on time. On Tuesday, I have a UILocalNotification to bring in the trash bins for Tuesday at 5pm, again with no repeat interval. When that one fires, I will see the correct notification for NOW, but also below the current notification will be another notification to take the trash out 1 Day Ago. I have not rescheduled this notification. It appears to be the notification from yesterday.
It is very bizarre and I cannot reproduce it on any sort of consistent basis. I thought that maybe there were some old notifications being added somehow so I added some logic to run through all scheduled notifications and remove any that had a fire date that was in the past but that did not help. Anyone else ever see this problem? Is there some manual clearing of a notification that I need to do when one fires?
EDIT: Added some of the scheduling code
//only schedule if the alert date is later than now
if ([AMDateUtil isNowEarlierThanDate:alertDate]) {
//create the notification and setup some properties
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = alertDate;
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.alertAction = nil;
localNotification.soundName = UILocalNotificationDefaultSoundName;
//add the local notification
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
We had the same issue, but managed to fix it by batching our calls to the API. Instead of calling scheduleLocalNotification each time, instead build an array of all notifications you want to be scheduled. Like this:
NSMutableArray *notifications = [[[UIApplication sharedApplication] scheduledLocalNotifications] mutableCopy];
// add notifications to the mutable array
// same goes for removing notifications
[[[UIApplication sharedApplication] setScheduledLocalNotifications:notifications];
Also make sure you are calling it from the main thread.
Our problems seem to have gone away after doing this.
The first thing that I can think of is to check the repeatInterval of your notifications. It seems that you may want your repeatInterval to be weekly, but the interval seems to be set to daily. To set the repeatInterval to weekly use:
localNotification.repeatInterval = NSWeekCalendarUnit;
Perhaps in some places you may be accidentally using
localNotification.repeatInterval = NSWeekdayCalendarUnit;
which repeats once a day.
If that is not the issue, then perhaps if you post some of your code where you schedule the notifications someone could help you. If a notification has a repeat interval of 0 (or not repeating) then you should not have to manually clear it. If the repeat interval is not 0 then you will have to manually clear it to get it to stop repeating.

How to know UILocalNotification remaining time before fires?

I would like to know remaining time before UILocalNotification fires and then show it in some UIabel in real time.
How can I do that?
Notification has property fireDate - The date and time when the system should deliver the notification.
So you can use it and current date for calculation remained time.
Following two methods available to get the Registered LocalNotifications, get the Object of it and access FireDate, and calculate your time
To get all the notifications
[[UIApplication sharedApplication] scheduledLocalNotifications];
To get particular notification object if you know Which object to get.
[[UIApplication sharedApplication] scheduleLocalNotification:yourNOtificationObject];

Resources