I am working on a application that reminds about the expiry date. I have implemented the same using UILocalNotification with repeat Interval (NSMonthCalendarUnit, NSDayCalendarUnit,NSDayCalendarUnit). For example I have the fire date on 01-01-2012 and the repeat interval is NSDayCalendarUnit and the end date is 12-12-2012, is it possbile to cancelLocalNotification: on expiry.
here is the code:-
- (void) scheduleNotificationOn:(NSDate*) fireDate
text:(NSString*) alertText
action:(NSString*) alertAction
sound:(NSString*) soundfileName
launchImage:(NSString*) launchImage
andInfo:(NSDictionary*) userInfo
{
userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
txtExpiryDate.text, #"ExpiryDate",
txtRegNo.text , #"RegNo",
nil];
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = fireDate;
localNotification.timeZone = [NSTimeZone systemTimeZone];
localNotification.userInfo = userInfo;
localNotification.alertBody = alertText;
localNotification.alertAction = alertAction;
NSLog(#"Repeat Type:%#",txtRepeat.text);
if([txtRepeat.text isEqualToString:#"Every Week"])
{
NSLog(#"Every Week");
localNotification.repeatInterval = 256;
}
else if([txtRepeat.text isEqualToString:#"Every Month"])
{
NSLog(#"Every Month");
localNotification.repeatInterval = NSMonthCalendarUnit;
}
else if([txtRepeat.text isEqualToString:#"Every Day"])
{
NSLog(#"Every Day");
localNotification.repeatInterval = NSDayCalendarUnit;
}
if(soundfileName == nil)
{
localNotification.soundName = UILocalNotificationDefaultSoundName;
}
else
{
localNotification.soundName = soundfileName;
}
NSLog(#"appDelegate.BadgeNumber:%d",appDelegate.BadgeNumber);
localNotification.alertLaunchImage = launchImage;
appDelegate.BadgeNumber = appDelegate.BadgeNumber + 1;
localNotification.applicationIconBadgeNumber = appDelegate.BadgeNumber;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
[localNotification release];
}
I have worked by comparing current date to expiry date. But this work only if the app is in foreground and i cannot cancelnotification not background for a particular date. Please find the below code for the same:-
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
BadgeNumber = 0;
application.applicationIconBadgeNumber = BadgeNumber;
NSArray *localNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
NSDateFormatter *formatter =[[[NSDateFormatter alloc]init] autorelease];
[formatter setDateFormat:#"dd/MM/yyyy"];
NSLog(#"localNotifications Count %d",localNotifications.count);
for (UILocalNotification *notify in localNotifications)
{
//notify.applicationIconBadgeNumber = 0;
NSString *ExpiryDateString = [notify.userInfo objectForKey:#"ExpiryDate"];
NSDate *ExpiryDate = [formatter dateFromString:ExpiryDateString];
NSDate * NextFireDate = nil;
NSLog(#"Expiry Date:%#",ExpiryDateString);
if(notify.repeatInterval == NSDayCalendarUnit)
{
NSLog(#"Repeat Every Day");
NextFireDate = [[NSDate date] dateByAddingDays:1];
NSLog(#"Next FireDate: %#",[formatter stringFromDate:NextFireDate]);
}
if(notify.repeatInterval == NSWeekCalendarUnit)
{
NSLog(#"Repeat Every Day");
NextFireDate = [[NSDate date] addTimeInterval:D_WEEK];
NSLog(#"Next FireDate: %#",[formatter stringFromDate:NextFireDate]);
}
if(notify.repeatInterval == NSMonthCalendarUnit)
{
NSLog(#"Repeat Every Day");
//NextFireDate = [[NSDate date] addTimeInterval:D_Month];
NextFireDate = [self CalculateExipiryDateForMonth];
NSLog(#"Next FireDate: %#",[formatter stringFromDate:NextFireDate]);
}
NSComparisonResult result = [NextFireDate compare:ExpiryDate];
NSLog(#"NSComparisonResult:%d",result);
if(result == NSOrderedDescending)
{
NSLog(#"Cancell......... Notification");
NSLog(#"notify :::%#",notify);
}
else
{
NSLog(#"Re-Schedule Notification");
BadgeNumber = BadgeNumber + 1;
notify.applicationIconBadgeNumber = BadgeNumber;
NSLog(#"BadgeNumber:%d",BadgeNumber);
[[UIApplication sharedApplication] scheduleLocalNotification:notify];
}
}
}
-(NSDate*) CalculateExipiryDateForMonth
{
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.month = 1;
NSDate *nextMonth = [gregorian dateByAddingComponents:components toDate:[NSDate date] options:0];
[components release];
NSDateComponents *nextMonthComponents = [gregorian components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:nextMonth];
NSDate *expiryDay = [gregorian dateFromComponents:nextMonthComponents];
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
dayComponent.day = -1;
NSDate *NewExpiry = [gregorian dateByAddingComponents:dayComponent toDate:expiryDay options:0];
[gregorian release];
[dayComponent release];
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:#"dd/MM/yyyy"];
NSLog(#"Next Exipiry Date -----:%#",[formatter stringFromDate:NewExpiry]);
return NewExpiry;
}
In short, no, you cannot cancel a UILocalNotification while your app is running in the background.
**Apple RESPONSE:-**
I'm responding to your question about UILocalNotification.
At this time UILocalNotification does not have a way to specify an expiry
date or number of repetitions before the notification is automatically
canceled.
The closest you can get today is to schedule up to 64 individual
notifications instead of using the repeat interval.
But you're correct; if the user doesn't ever bring your app to the
foreground, it won't have the opportunity to cancel or reschedule local
notifications.
I highly recommend that you file an enhancement request at <
https://developer.apple.com/bugreporter> asking for this functionality in a
future release of iOS.
You also asked about what happens to local notifications when the user
removes your app from the device. iOS stores the local notifications so
that they are still scheduled if the user deletes and then reinstalls the
app.
When your app runs, it can check the scheduledLocalNotifications property
of UIApplication and remove any notifications that are no longer relevant.
Best regards,
--gc
Garth Cummings
Apple Developer Technical Support
Related
How to notify every month notification.
-(void)applicationDidEnterBackground:(UIApplication *)application {
NSDateFormatter *format = [[NSDateFormatter alloc] init];
format.dateFormat = #"dd-MM-yyyy";
NSComparisonResult result = [[format stringFromDate:[NSDate new]] compare:[format stringFromDate:[self lastDayOfMonthOfDate:[NSDate date]]]];
if (result == NSOrderedSame){
if (![USERDEFAULTS boolForKey:#"IS_MONTH"]) {
[self getNotifiedForEveryMonth:nil];
}}
else{
[USERDEFAULTS setBool:NO forKey:#"IS_MONTH"];
}
}
// getNotifiedForEveryMonth
-(void)getNotifiedForEveryMonth:(id)userinfo{
// Schedule the notification
[USERDEFAULTS setBool:YES forKey:#"IS_MONTH"];
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate date];
localNotification.alertBody = #"Every Month Notificiation";
localNotification.alertAction = #"Show me the item";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
}
-(NSDate*)lastDayOfMonthOfDate:(NSDate *)date
{
// NSGregorianCalendar
NSInteger dayCount = [self numberOfDaysInMonthCountForDate:date];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
NSDateComponents *comp = [calendar components:
NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitDay fromDate:date];
[comp setDay:dayCount];
return [calendar dateFromComponents:comp];
}
-(NSInteger)numberOfDaysInMonthCountForDate:(NSDate *)date
{
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:TIME_ABSOLUTE]];
NSRange dayRange = [calendar rangeOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitMonth
forDate:date];
return dayRange.length;
}
The above code with i try to notify the notification.
Its getting notification every month end day,
If user close the notification and then next month comes the notifcation is not notifying.
After installation the application, how to notify the user very month end day a notification message.
Your inputs are appreciated.
1) If you want fire notifications every month, even if user didn't open an app more then month, you should set several notifications (up to 60). For example, set notifications for next 12 month.
2) You should set notifications by this code (on loop, 12 times):
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
3) Before you set new notifications, remove old with this code:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
ViewController:
- (void)viewDidLoad
{
`[super viewDidLoad];`
dateFormatter.dateFormat = #"dd/MM";
timeFormatter.dateFormat = #"HH:mm";
NSString *dateString = [dateFormatter stringFromDate:dateTime];
NSString *timeString = [timeFormatter stringFromDate:dateTime];
if ([dateString isEqual:#"23/06"]) {
if ([timeString isEqual:#"23:30"]) {
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = dateTime;
localNotification.alertBody = [NSString stringWithFormat:#"It's 11:30 PM 23th June!"];
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}
}
AppDelegate:
[[UIApplication sharedApplication] scheduledLocalNotifications];
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge| UIUserNotificationTypeSound categories:nil]];
}
The notification isn't receiving when time equals string and date.
Please help!
The core issue with this (aside from using strings to compare dates) is that viewDidLoad gets called only once in this application. Because you are only scheduling the local notification when the current date is 11:30, it will never get called, since you also set the fireDate to the same exact time.
The thing is, local notifications get scheduled prior to the event. In fact, you should set dateTime to the desired schedule time and then set the local notification.
For example:
- (void)viewDidLoad {
[super viewDidLoad];
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
[dateComponents setYear:2016];
[dateComponents setMonth:6];
[dateComponents setDay:23];
[dateComponents setHour:23];
[dateComponents setMinute:30];
dateTime = [[NSCalendar currentCalendar] dateFromComponents:dateComponents];
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = dateTime;
localNotification.alertBody = [NSString stringWithFormat:#"It's 11:30 PM 23th June!"];
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
Based on your uploaded code, it looks like you are going to be grabbing the date from the datePicker, so eventually, you will move the UILocalNotification portion to the button method and use [datePicker date] as the fireDate. Meaning, you can ignore the dateComponents part.
I'd like to create local notification with custom interval repeat (every 20 days for example).
I know we have NSDayCalendarUnit, kCFCalendarUnitMonth ... but I hope to set repeat interval at 20 days.
I don't want to create a notification for every day.
My real need is to repeat a notification for consecutive 21 days, then don't launch it for 7 days later, then a new 21 days with notification and 7 days without ... etc.
I should schedule all these days even if application is inactive.
To do this I decide to create a 21 notifications since a fire date with repeatInterval = 28days (here is the problem)
Try this, you can change the intervall by selecting setDay, setMonth, .... :
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:3];
NSDate *date3Days = [calendar dateByAddingComponents:components
toDate:[NSDate date]
options:0];
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];
if ( oldNotifications ) {
[app cancelAllLocalNotifications];
app.applicationIconBadgeNumber = 0;
}
UILocalNotification* notifyAlarm = [[UILocalNotification alloc] init];
if (notifyAlarm) {
notifyAlarm.fireDate = date3Days;
notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
notifyAlarm.alertBody = NSLocalizedString( #"Push message", #"");
notifyAlarm.soundName = #"sound.wav";
[app scheduleLocalNotification:notifyAlarm];
}
If you want to set a specific time after which the UILocalNotifications should appear you can create a method of the above solution and loop over an array which indicates the days you like to show a notification:
NSArray *arrayNumbers = #[#5, #7, #14, #21, #30, #60, #90, #120];
NSDictionary *dictNotifications =
[[NSUserDefaults standardUserDefaults] objectForKey:kUserDefAppStarts];
for ( NSNumber *bla in arrayNumbers ){
NSString *strKey = [NSString stringWithFormat:#"%#%#", kUserDefAppStarts, bla];
NSDictionary *dict = dictNotifications[strKey];
NSString *strMessageQuestion = dict[kKeyMessage];
[self createNotificationWithNumberOfDays:[bla integerValue]
andMessage:strMessageQuestion
userInfo:dict];
}
And here is the method you have to call
+ (void)createNotificationWithNumberOfDays:(int)days
andMessage:(NSString *)message
userInfo:(NSDictionary *)dict{
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:days];
NSDate *dateAlert = [gregorian dateByAddingComponents:components toDate:[NSDate date] options:0];
UIApplication *app = [UIApplication sharedApplication];
UILocalNotification *notifyAlarm = [[UILocalNotification alloc] init];
if( notifyAlarm ){
[notifyAlarm setFireDate:dateAlert];
[notifyAlarm setTimeZone:[NSTimeZone defaultTimeZone]];
[notifyAlarm setSoundName:#"soundname"];
[notifyAlarm setAlertBody:message];
[notifyAlarm setUserInfo:dict];
[app scheduleLocalNotification:notifyAlarm];
}
}
If you wanted to schedule a UILocalNotification for 20 days in the future from the current time you would do this:
NSCalendar *currentCalendar = [NSCalendar currentCalendar];
NSDateComponents *dateComp = [[NSDateComponents alloc] init];
int desiredAmountOfMonths = 4;
for (int month = 0; month < desiredAmountOfMonths; month++)
{
dateComp.month = month;
dateComp.day = 20;
NSDate *fireDate = [currentCalendar dateByAddingComponents:dateComp toDate:[NSDate date] options:NSCalendarMatchNextTimePreservingSmallerUnits];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = fireDate;
notification.timeZone = [NSTimeZone defaultTimeZone];
}
You will need to modify the UILocalNotification for what message, sound, etc and then fire the notification once done customizing.
I want to remind user to take photo everyday. I use code for every day local push notificatons
UILocalNotification *everyDayNotification = [[UILocalNotification alloc] init];
everyDayNotification.repeatInterval = NSDayCalendarUnit;
NSDate *currentDate = [[NSDate alloc] init];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:currentDate];
int hour = [hours_string intValue];
int minutes = [minutes_string intValue];
[components setHour:hour];
[components setMinute:minutes];
NSDate *today10am = [calendar dateFromComponents:components];
everyDayNotification.fireDate = today10am;
everyDayNotification.timeZone = [NSTimeZone defaultTimeZone];
everyDayNotification.alertBody = #"It us time";
everyDayNotification.alertAction = #"Action";
everyDayNotification.soundName = UILocalNotificationDefaultSoundName;
everyDayNotification.applicationIconBadgeNumber = 1;
NSDictionary *userDict = [NSDictionary dictionaryWithObject:#"EveryDay"
forKey:#"RemindNotificaion"];
everyDayNotification.userInfo = userDict;
[[UIApplication sharedApplication] scheduleLocalNotification:everyDayNotification];
But if the user has made the photo before it is time to notice that. I want to skip the notification for today. Is that possible?
I would suggest making an individual UILocalNotification for each day and storing them, perhaps in an array in NSUserDefaults. Then you can cancel a notification with the following:
[[UIApplication sharedApplication] cancelLocalNotification:notficiationInstance];
this is how i setting my local notification, all work well.
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorianCalendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit |NSHourCalendarUnit|NSMinuteCalendarUnit fromDate:[_datePicker date]];
NSLog(#"%#",[_datePicker date]);
NSDate *fireDate = [gregorianCalendar dateFromComponents:components];
NSLog(#"Fire date : %#",fireDate);
// check if the time is already passed
if ([fireDate compare:[NSDate date]] == NSOrderedAscending)
{
// if it does add 1 day
components.day = components.day+1;
fireDate = [gregorianCalendar dateFromComponents:components];
}
NSLog(#"Fire date : %#",fireDate);
for (int i = 0; i < 7; i++) {
NSDate *scheduleDate = [fireDate dateByAddingTimeInterval:(i * 24.0f * 3600.0f)];
NSDateComponents *componentsForEachDay = [gregorianCalendar components:NSWeekdayCalendarUnit fromDate:scheduleDate];
if (componentsForEachDay.weekday != 7) { // To skip Saturday
localNotification = [[UILocalNotification alloc] init];
localNotification.repeatInterval = NSWeekCalendarUnit;
localNotification.fireDate = fireDate;
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.alertBody = #"test";
localNotification.repeatInterval = NSCalendarUnitDay;
localNotification.applicationIconBadgeNumber = 0;
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"%i",i] forKey:[NSString stringWithFormat:#"bracletNotif%i",i]];
localNotification.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
i always get only the last object in userInfo.. how can i store all the scheduleLocalNotification in the userInfo for later cancel?
how does its work? before every scheduleLocalNotification i need so save an object and key? or can i save them all at one?
because i already tried to do that, i got all the userInfo objects but could not delete them :/
SOS please.
If you are trying to cancel all braclet notifications instead of giving them all a different key/value you could do this instead:
localNotification.userInfo = #{#"category": #"bracelet"};
To cancel:
[[[UIApplication sharedApplication] scheduledLocalNotifications] enumerateObjectsUsingBlock:^(UILocalNotification *notification, NSUInteger idx, BOOL *stop) {
NSDictionary *userInfo;
if((userInfo = [notification userInfo])){
NSString *categoryValue;
if((categoryValue = [userInfo objectForKey:#"category"]) && [categoryValue isEqualToString:#"bracelet"]){
[[UIApplication sharedApplication] cancelLocalNotification: notification];
}
}
}];
What we are doing here is grouping all notifications into the category: bracelet. I have chosen the key category due to future changes in the sdk. We could schedule more notifications e.g. category: neckless
Then when required we loop through all the UILocalNotifications and scan for the category key in the notifications userInfo, in the example above if the keys value equals bracelet, then we cancel the notification.
if((categoryValue = [userInfo objectForKey:#"category"]) && [categoryValue isEqualToString:#"bracelet"]){
This line of code is checking whether a key of category exists (i.e. not nil) whilst assigning it to the variable categoryValue then the if statement is immediately checking if the categoryValue is equal to bracelet
It could have been rewritten as:
NSString *categoryValue = [userInfo objectForKey:#"category"];
if(categoryValue && [categoryValue isEqualToString:#"bracelet"]){