I'm having significant trouble with my iOS local notifications. I have an app, written in Objective-C, that allows the user to pick the time the notification fires, as well as the days of the week (it should fire every week on the specified days at the specified time). I have no trouble with the time of day the notification fires, that works correctly. However, when I specify the day(s) it should fire, odd things happen. At first, they fire once, but every day rather than only the specified days. Then, after the first week, they fire every day, but not only once. Instead, they fire as many times as days are specified (so if Sunday, Monday and Tuesday are chosen, each day, the user will receive 3 consecutive notifications at the specified time).
This is the code I'm using to set up the notifications.
When the "Save" button is tapped, the first thing that happens is a clearing of all notifications, to make way for the new ones.
//cancels all notifications upon save
[[UIApplication sharedApplication] cancelAllLocalNotifications];
Next, I use NSDate, NSCalendar and NSCalendarComponents to get the specifics for the current time, as well as the components from my UIPicker (which is used to select the time of day)
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitWeekOfYear|NSCalendarUnitWeekday|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:now];//get the required calendar units
Then, I get the units for the time from the UIPicker, and the actual time, also from the picker.
NSDateComponents *pickedComponents = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:picker.date];
NSDate *minuteHour = [calendar dateFromComponents:pickedComponents];
[[NSUserDefaults standardUserDefaults] setObject:minuteHour forKey:@"FireTime"];
After that, I set the text I want to show up in the notification
NSString *reminder = @"Reminder text!";
Next is the actual setup of the notification. They're all the same (with the day of the week changed, of course), so I'll just show the one for Sunday.
//sunday
UILocalNotification *localNotificationSunday = [[UILocalNotification alloc] init];
if ([sundayTempStatus isEqual:@"1"])
{
//permanently save the status
[[NSUserDefaults standardUserDefaults] setObject:@"1" forKey:@"Sunday"];
//set up notifications
//if it is past sunday, push next week
if (components.weekday > 1)
{
components.day = components.day + 7; //if already passed sunday, make it next sunday
}
//components.day = 1;
components.hour = [pickedComponents hour];
components.minute = [pickedComponents minute];
NSDate *fireDate = [calendar dateFromComponents:components];
localNotificationSunday.fireDate = fireDate;
localNotificationSunday.alertBody = reminder;
localNotificationSunday.timeZone = [NSTimeZone systemTimeZone];
localNotificationSunday.repeatInterval = NSCalendarUnitWeekOfYear;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotificationSunday];
}
else
{
[[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:@"Sunday"];
}
Any help is greatly appreciated, and if any additional info or code is needed, I'll gladly provide it.
When code becomes repetitive, it often becomes more error-prone. I've written out a simple method that should take care of the scheduling of the reminders.
- (void)scheduleNotificationForDayOfWeek:(int)dayOfWeek withPickedComponents:(NSDateComponents *)pickedComponents andReminderString:(NSString *)reminderString {
NSCalendar *calendar = [NSCalendar currentCalendar];
UILocalNotification *notification = [[UILocalNotification alloc] init];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitWeekOfYear|NSCalendarUnitWeekday|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:[NSDate date]];
components.hour = [pickedComponents hour];
components.minute = [pickedComponents minute];
NSDateComponents *additionalComponents = [[NSDateComponents alloc] init]; // to be added onto our date
if ([components weekday] < dayOfWeek) {
additionalComponents.day = (dayOfWeek - [components weekday]); // add the number of days until the next occurance of this weekday
} else if ([components weekday] > dayOfWeek) {
additionalComponents.day = (dayOfWeek - [components weekday] + 7); // add the number of days until the next occurance of this weekday
}
NSDate *fireDate = [calendar dateFromComponents:components];
fireDate = [calendar dateByAddingComponents:additionalComponents toDate:fireDate options:0]; // add on our days
notification.fireDate = fireDate;
notification.alertBody = reminderString;
notification.timeZone = [NSTimeZone systemTimeZone];
notification.repeatInterval = NSCalendarUnitWeekOfYear;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
What this does is actually pretty simple. Call it with a day of the week (e.g., 0 = Sunday, 1 = Monday), and it will schedule a repeating reminder for that day. I couldn't reproduce your issues in my tests with your Sunday code, so I figured that somewhere among repeating that code you must have made an error.
In this method, the fire date getting has been simplified. It uses NSDateComponents to easily get the next occurrence of that weekday. Call it like this: [self scheduleNotificationForDayOfWeek:0 withPickedComponents:pickedComponents andReminderString:@"Hello, world!"];
(which would show "Hello, world!" every Sunday at the specified components)
With this snippet, you should be able to get rid of most of the repeated statements in your code, and simplify how setting notifications is done. For me, this worked perfectly.