I'm working with local notification. I got the notifications working. But I'm struggling with setting the correct date.
Let me first sketch the situation. I am making a festival app. This festival is on the 28-29-30 JUNE. Every artist object has an art_day. This can be 1 - 2 or 3.
1 --> 28 JUNE
2 --> 29 JUNE
3 --> 30 JUNE
I want a local notification 15 min before the start time of the artist. So this is how I'm making my fireDate of the notification.
-(NSDate *)getDateForArtist:(Artists *)artist{
int day = [artist.art_day intValue];
int timeStart = [artist.art_timestart intValue];
NSString *timeStart2 = [self getTimeStamp:artist.art_timestart];
int hours = [[timeStart2 substringWithRange:NSMakeRange(0,2)]intValue];
int minutes = [[timeStart2 substringWithRange:NSMakeRange(2,2)]intValue];
//in my db timeStart looks like 1530 --> 15h30 (I know bad db structure!!!)
int day2;
if(day == 1){
NSLog(#"day is 28");
day2 = 28;
}else if (day == 2){
NSLog(#"day is 29");
day2 = 29;
}else{
NSLog(#"day is 30");
day2 = 30;
}
NSDateComponents *comps = [[NSDateComponents alloc] init];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"GMT"];
[comps setTimeZone:timeZone];
[comps setDay:day2];
[comps setMonth:06];
[comps setYear:2013];
[comps setHour:hours];
[comps setMinute:minutes];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
NSLog(#"date is %#",date);
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setMinute:-15];
NSDate *date2 = [gregorian dateByAddingComponents:offsetComponents toDate:date options:0];
NSLog(#"date -15 min %#", date2);
return date2;
}
This is how I create my notification
UIApplication* app = [UIApplication sharedApplication];
UILocalNotification* notifyAlarm = [[UILocalNotification alloc]
init];
if (notifyAlarm)
{
NSDate *datePush = [self getDateForArtist:artist];
NSLog(#"artist plays on: %# on hour %#",artist.art_day,artist.art_timestart);
NSLog(#"Push notification should send on: %#",datePush);
notifyAlarm.fireDate = datePush;
NSDictionary *dicNotification = [[NSDictionary alloc]initWithObjectsAndKeys:artist.art_id,#"pushKey", nil];
notifyAlarm.userInfo = dicNotification;
notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
notifyAlarm.repeatInterval = 0;
notifyAlarm.soundName = #"Glass.aiff";
notifyAlarm.alertBody = [NSString stringWithFormat:#"%# starts in 15 minutes",artist.art_name];
[app scheduleLocalNotification:notifyAlarm];
NSLog(#"Added");
}
But for some reason I don't get any push notification. Or I think that the date is not correct.
This is what I get from my logs
2013-06-29 15:37:17.801 genkonstage[14120:907] artist plays on day: 2 on hour 2020
2013-06-29 15:37:17.803 genkonstage[14120:907] Push notification should send on: 2013-06-29 20:05:00 +0000
Can anybody help me with this?
Related
Here's an example of what I want. The user may set up an alarm in my app for 1 minute in the future, so they can test it out. The time might be 19:23, so they'll set the alarm to 19:24, in which case I want it to be triggered on the next occurrence of 19:24 - in 1 minute's time.
If they set the alarm for 8am, I don't want it to set to 8am on the current day, but on the next occurrence of 8am - on following day.
How can I get it to aim for the next occurrence of the time chosen?
Assuming that the alarm time is given as "hour" and "minute", the following code
should produce the desired result:
NSDate *now = [NSDate date];
// Example values for testing:
NSUInteger alarmHour = 10;
NSUInteger alarmMinute = 5;
// Compute alarm time by replacing hour/minute of the current time
// with the given values:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *comp = [cal components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit
fromDate:now];
[comp setHour:alarmHour];
[comp setMinute:alarmMinute];
NSDate *alarm = [cal dateFromComponents:comp];
// If alarm <= now ...
if ([alarm compare:now] != NSOrderedDescending) {
// ... add one day:
NSDateComponents *oneDay = [[NSDateComponents alloc] init];
[oneDay setDay:1];
alarm = [cal dateByAddingComponents:oneDay toDate:alarm options:0];
}
More tersely, what #HotLicks suggests:
NSDate *userEnteredDate;
NSDate *now = [NSDate date];
if (now == [now laterDate:userEnteredDate]) {
NSDateComponents *components = [[NSCalendar currentCalendar] components:255 fromDate:userEnteredDate]; // 255= the important component masks or'd together
components.day += 1;
userEnteredDate = [[NSCalendar currentCalendar] dateFromComponents:components];
}
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
NSDateFormatter *formatter_ = [[NSDateFormatter alloc]init];
NSDate *alarmDate = [formatter dateFromString:#"enter your users alarm time-examle(2345)"];
NSDate *currentDate = [NSDate date];
[formatter setDateFormat:#"HHmm"];
NSDate *finalDate;
if ([[formatter stringFromDate:currentDate] intValue] > [[formatter stringFromDate:alarmDate] intValue]) {
[formatter setDateFormat:#"HH:mm"];
[formatter_ setDateFormat:#"dd MM yyyy"];
NSDate *date = [currentDate dateByAddingTimeInterval:60*60*24*1];
finalDate = [formatter_ dateFromString:[NSString stringWithFormat:#"%# %#",[formatter stringFromDate:alarmDate],[formatter_ stringFromDate:date]]];
}else{
finalDate = [formatter_ dateFromString:[NSString stringWithFormat:#"%# %#",[formatter stringFromDate:alarmDate],[formatter_ stringFromDate:currentDate]]];
}
I am trying to get my app working with local notifications. But I can't get my head arround. I'm looking after the problem for days. I have in my core database an entity Favorites (all my favorite artists) and an entity Artists (artist his detail information.
I have a button to set the local notification on. When I press the button, I do the following.
-(void)addLocalNotifications{
GenkonStageDataModel *model = [[GenkonStageDataModel alloc]init];
NSMutableArray *allFavorites = [model getAllFavorites];
NSLog(#"allFavoriets count is %d",allFavorites.count);
UIApplication* app = [UIApplication sharedApplication];
for (int i = 0; i<allFavorites.count; i++){
Favorites *favorite = [allFavorites objectAtIndex:i];
int artId = [favorite.fav_art_id intValue];
Artists *artist = [model getArtistById:artId];
UILocalNotification* notifyAlarm = [[UILocalNotification alloc]
init];
if (notifyAlarm)
{
NSDate *datePush = [self getDateForArtist:artist];
NSLog(#"Push notification should send on: %#",datePush);
notifyAlarm.fireDate = datePush;
NSDictionary *dicNotification = [[NSDictionary alloc]initWithObjectsAndKeys:artist.art_id,#"pushKey", nil];
notifyAlarm.userInfo = dicNotification;
notifyAlarm.repeatInterval = 0;
notifyAlarm.soundName = #"Glass.aiff";
notifyAlarm.alertBody = [NSString stringWithFormat:#"%# starts in 15 minutes",artist.art_name];
[app scheduleLocalNotification:notifyAlarm];
NSLog(#"Added");
}
}
}
What this method does is the following.
1. Get all the favorites
2. Loop through the favorites and get the artists linked with that favorite by ID
3. Get the date for when the local notification should be sent
4. Set in the userInfo dictionary the artist ID (I do this for deleting the local notification when I want to)
5. Shedule the local notification.
Now all these are added (I think) because it loops correctly through the array and also always gives me the correct date and "ADDED" in my log.
But now when I change my device it's dateTime to the time that I normally should receive the local notification. I do not receive anything!!!!
I also changed my date to 2hours earlier for the correct time. Because when I was testing with an example local notification. They setted the fireDate like this.
NSDate *alertTime = [NSDate date];
This caused that the local notifcation was sent immedialty after that I clicked the button. But when I logged this I noticed that is was 2 hours before the actual time... ?
I seriously hope that anybody can help me with this problem!
Thanks in advance!
EDIT
This is how I get my fireDate
-(NSDate *)getDateForArtist:(Artists *)artist{
int day = [artist.art_day intValue];
int timeStart = [artist.art_timestart intValue];
NSString *timeStart2 = [self getTimeStamp:artist.art_timestart];
int hours = [[timeStart2 substringWithRange:NSMakeRange(0,2)]intValue];
int minutes = [[timeStart2 substringWithRange:NSMakeRange(2,2)]intValue];
int day2;
if(timeStart >=2400 ){
day = day++;
}
if(day == 1){
NSLog(#"day is 28");
day2 = 28;
}else if (day == 2){
NSLog(#"day is 29");
day2 = 29;
}else{
NSLog(#"day is 30");
day2 = 30;
}
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:day2];
[comps setMonth:06];
[comps setYear:2013];
[comps setHour:hours];
[comps setMinute:minutes];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
NSLog(#"date is %#",date);
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setMinute:-15];
NSDate *date2 = [gregorian dateByAddingComponents:offsetComponents toDate:date options:0];
NSLog(#"date -15 min %#", date2);
return date2;
}
And I get this LOG
2013-06-30 14:37:13.910 genkonstage[1633:907] date is 2013-06-30 16:50:00 +0000
2013-06-30 14:37:13.913 genkonstage[1633:907] Push notification should send on: 2013-06-30 16:35:00 +0000
From your comment (works 10 seconds ahead) it sounds like it's working fine.
When you change the device's date/time how are you doing it? forwards in time? backwards? are you altering the timezone?
Also are you scheduling notifications with a date/time that is in the past? If you specify a date that is in the past (or nil) the notification is delivered immediately.
The fire date is evaluated using the timeZone property of the UILocalNotification object, so if you do not set the timezone the fire data is considered to be GMT time. try:
notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
If you set a fire date of 09:00 and then move to a different time zone the alarm will still fire at 09:00 in the new time zone.
It was quite a bit hard to enable local notifications for my application.
And yeah, I set up my project with a local notification witch I want to schedule after the app had been closed for one week. For example if the user opens the app on saturday 8th, the local notification should appear on the next saturday the 15th, but the time should changed. For example he/she closes the app at 8pm and I don't want to disturb them at 8pm next week so I want to display every notification at 6pm or something like that.
Now you know my problem and here is my code I'm using:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar] ;
NSDateComponents *componentsForReferenceDate = [[NSDateComponents alloc] init];
//set day (saturday)
[componentsForReferenceDate setDay:26] ;
[componentsForReferenceDate setMonth:1] ;
[componentsForReferenceDate setYear:2013] ;
[componentsForReferenceDate setHour: 17] ;
[componentsForReferenceDate setMinute:30] ;
[componentsForReferenceDate setSecond:00] ;
NSDate *fireDateOfNotification = [calendar dateFromComponents: componentsForReferenceDate];
// Create the notification
UILocalNotification *notification = [[UILocalNotification alloc] init] ;
notification.fireDate = fireDateOfNotification ;
notification.timeZone = [NSTimeZone localTimeZone] ;
notification.alertBody = [NSString stringWithFormat: #"Du wirst vermisst! \nDeine App braucht dich, schreibe sie zu Ende!"] ;
notification.alertAction = #"Zurückkehren";
notification.userInfo= [NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"Some information"] forKey:#"information"];
notification.repeatInterval= NSWeekCalendarUnit ;
notification.soundName = #"Appnotifisound.wav";
notification.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:notification] ;
}
I know that there have to be more methods for deleting the badge and the didrecievenotification, but I let them out because there are not important for this.
With this code I managed to schedule the notification on every saturday at 5:30pm (Germany). But I wanted to schedule it only once, when the app had been closed for exactly one week. Is that somehow possible? I would be glad if someone could correct my code or give me a solution for this.
Best regards and thank you for reading this long post,
Noah
You should unschedule previous notification.
for (UILocalNotification *lNotification in [[UIApplication sharedApplication] scheduledLocalNotifications])
{
if ([[lNotification.userInfo valueForKey:#"information"] isEqualToString:#"Some information"])
{
[[UIApplication sharedApplication]cancelLocalNotification:lNotification];
break;
}
}
Set fire date by below way:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *componentsForReferenceDate = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
fromDate:[NSDate date]];
[componentsForReferenceDate setHour: 17] ;
[componentsForReferenceDate setMinute:30] ;
[componentsForReferenceDate setSecond:00] ;
NSDate *tempDate = [calendar dateFromComponents: componentsForReferenceDate];
[componentsForReferenceDate release];
NSDateComponents *comps = [[NSDateComponents alloc]init];
[comps setDay:7];
NSDate *fireDateOfNotification = [calendar dateByAddingComponents:comps
toDate:tempDate options:0]
[comps release];
The notification.repeatInterval = NSWeekCalendarUnit is causing this. use:
notification.repeatInterval = 0;
This prevent repeating (source).
Here is the Swift 2.0 adaptation
Unscheduling the current notification
for notifMe:AnyObject in UIApplication.sharedApplication().scheduledLocalNotifications!{
let title = notifMe.userInfo["information"] as? String
if title == "some information"{
UIApplication.sharedApplication().cancelLocalNotification(notifMe as! UILocalNotification)
}
}
Setting the date for the notification
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(NSCalendarUnit.Year.union(NSCalendarUnit.Month).union(NSCalendarUnit.Day),fromDate: date)
components.hour = 17
components.minute = 30
components.second = 00
let tempDate = calendar.dateFromComponents(components)!
let comps = NSDateComponents()
comps.day = 7
let fireDateOfNotification = calendar.dateByAddingComponents(comps, toDate: tempDate, options:[])
I have two time.. one fetched directly as string(#"21:00") and other is the current time. I want to display a count down timer showing how much time left from now to reach 21:00.
For eg: the label i use should display "You have 3hrs and 30 minutes left.." if the current time is 17:30.
thanks..
OK have completely revised my answer, and have created a complete solution to the problem, with fully tested sample code available on github.
Enjoy :)
Something like this should do it. This will give you the remaining time in seconds. Then you just need to to standard timer stuff as indicated in other answers.
//assumption: targetTime is after now
NSString *targetTime = #"21:00";
//split our time into components
NSArray *timeSplit = [targetTime componentsSeparatedByString:#":"];
NSUInteger hours = [[timeSplit objectAtIndex:0] intValue];
NSUInteger minutes = [[timeSplit objectAtIndex:1] intValue];
NSDate *now = [NSDate date];
//split now into year month day components
NSCalendar *currentCalendar = [NSCalendar currentCalendar];
NSDateComponents *dateComponents = [currentCalendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:now];
//set our time components from above
[dateComponents setHour:hours];
[dateComponents setMinute:minutes];
NSDate *targetDate = [currentCalendar dateFromComponents:dateComponents];
//ensure target is after now
if ([targetDate timeIntervalSinceDate:now] < 0)
{
NSDateComponents *day = [[NSDateComponents alloc] init];
[day setDay:1];
targetDate = [currentCalendar dateByAddingComponents:day toDate:targetDate options:0];
}
NSTimeInterval timeRemaining = [targetDate timeIntervalSinceDate:now];
You create a NSTimer that fires every second:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(tickClock) userInfo:nil repeats:YES];
When it fires, you enter a method:
- (void)tickClock;
in there, you compare the date you have against the current date [NSData currentDate];
Like
NSTimeInterval distanceBetweenDates = [currentDate timeIntervalSinceDate:yourDate];
where distanceBetweenDates is specified in seconds.
To create a date from your string create a NSDate accordingly
NSInteger year = 2011;
NSInteger month = 8;
NSInteger day = 26;
NSInteger hour = 21;
NSInteger minute = 0;
NSInteger second = 0;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setYear:year];
[components setMonth:month];
[components setDay:day];
[components setHour:hour];
[components setMinute:minute];
[components setSecond:second];
NSDate *date = [calendar dateFromComponents:components];
[components release];
I want to set an alarm on a particular day. I don't understand how to set the value of kCFCalendarUnitWeekday. Here is my code:
NSDateComponents *components = [[NSDateComponents alloc] init];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
localNotif.fireDate = selected;
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.repeatCalendar = [NSCalendar currentCalendar];
if (isSun) {
[components setWeekday:1];
[localNotif setRepeatInterval:(NSInteger)components];
}
if (isMon) {
[components setWeekday:2];
[localNotif setRepeatInterval:(NSInteger)components];
}
Thank you.
Ok here is some code to get you in the right direction:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit|NSYearCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit fromDate:[NSDate date]];
// this will set the weekday to sunday
[components setWeekday:1];
// this is a new date on sunday
NSDate * newDate = [gregorian dateFromComponents:components];
You still have to find out whether the newDate is in the past, so it won't fire.
You actually have your code ready:
// ... init code ...
// *** this the important date ***
localNotif.fireDate = dateOnASunday;
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.repeatCalendar = NSWeekCalendarUnit;
... // add body etc. ...
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
Create a method around this snippet and pass the date with a parameter so you can reuse it. You only have to put in a date which should be the first fire date and it'll repeat every week. You just have to pass in a date on a Sunday or Wednesday.
You set a repeat interval like this:
// repeats notification on a weekly basis
localNotif.repeatCalendar = NSWeekCalendarUnit;
The repeatCalender property is of type NSCalendarUnit which is an enum.