My app runs constantly in kiosk mode. Once every 24 hours at a specific time I need to sync some data from core data to a web service.
I know how to do the sync piece but I don't know how to schedule the app to make the sync call at a specific time each day e.g. at 02:45 am.
Is it possible to do something like this when an app is running constantly?
Use Local Notifications. Here is a tutorial:http://www.icodeblog.com/2010/07/29/iphone-programming-tutorial-local-notifications/
Hope this helps u to start...
This as well:
Local Notifications
Background Tasks
Figured this out thanks to prompts from #lakesh. Posting solution in the case it helps somebody because I found NSNotification examples very difficult to understand at first.
In my main view controller I added the following method:
- (void)scheduleNotification
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *notif = [[UILocalNotification alloc] init];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
[components setDay: day];
[components setMonth: month];
[components setYear: year];
[components setHour: 02];
[components setMinute: 45];
[components setSecond: 0];
[calendar setTimeZone: [NSTimeZone systemTimeZone]];
NSDate *dateToFire = [calendar dateFromComponents:components];
notif.fireDate = dateToFire;
notif.timeZone = [NSTimeZone systemTimeZone];
notif.repeatInterval = NSDayCalendarUnit;
[[UIApplication sharedApplication] scheduleLocalNotification:notif];
}
This sets a fire date for the notification of today at 02:45 am and recurs daily.
In viewDidLoad in my view controller I call the above method:
[self scheduleNotification];
In the appdelegate I do the following:
- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification
{
application.applicationIconBadgeNumber = 0;
//call your method that you want to do something in your app
}
A simple solution is just to set NSTimer to check current date every minute (or every second, for example). If the current date is greater then required, then fire the method and update the required date.
Related
I'm facing troubles with my iOS app.
I would like to run a function every day even if my application is not on foreground on my IPhone. I tried to use NSTimer Object but it doesn't work if my app is in background.
How can I achieve this?
Note: My function will trigger a notification which differs based on the current day.
Paulw11 is correct. You can use background refresh and a local notification. The background fetch will be called randomly. Each time it is called the local notification will reset. This code is Obj C, it is a bit old, but the concept is the same.
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
BackgroundFetch * fetch = [[BackgroundFetch alloc] init];
[fetch fetchNewDataWithCompletionHandler:^(UIBackgroundFetchResult result) {
completionHandler(result);
} successDictionary:^(NSDictionary *responseDict) {
// Schedule local notification, in this case it is 9am
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *notif = [[UILocalNotification alloc] init];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
[components setDay: day];
[components setMonth: month];
[components setYear: year];
[components setHour: 9];
[components setMinute: 0];
[components setSecond: 0];
[calendar setTimeZone: [NSTimeZone systemTimeZone]];
NSDate *dateToFire = [calendar dateFromComponents:components];
notif.fireDate = dateToFire;
notif.timeZone = [NSTimeZone systemTimeZone];
notif.alertBody = [NSString stringWithFormat:#"%#: %#", responseDict[#"city"], responseDict[#"temp"]];
[[UIApplication sharedApplication] scheduleLocalNotification:notif];
}
You can't do it, because you can't run in the background, and in fact your app might not even be running. So rethink your architecture. For example, set up 30 notifications for the next 30 days, in case your app never runs during that time. That's what a notification is: a message delivered on your behalf by the system, which is always running.
I'm working on a feature that will allow users to schedule days and a time for receiving a notification.
So far, the time and message feature is working great. Where I am stuck at is the repeat interval.
Here's what I have tried:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSString *formatedDate = [dateFormatter stringFromDate:self.datePicker.date];
self.currentTimeLabel.text = formatedDate;
NSDate *date = [dateFormatter dateFromString:self.currentTimeLabel.text];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:date];
NSInteger hour = [components hour];
NSInteger minute = [components minute];
[components setHour:hour];
[components setMinute:minute];
if ([self.currentRepeatLabel.text containsString:#"Sun"]) {
[components setWeekday:0];
self.notification = [[UILocalNotification alloc] init];
self.notification.fireDate = [calendar dateFromComponents:components];
self.notification.repeatInterval = NSCalendarUnitDay;
[self.notification setAlertBody:[NSString stringWithFormat:#"A friendly reminder: %#", self.titleStringToDisplay]];
if (self.soundSwitch.isOn == YES) {
self.notification.soundName = #"soundeffect.wav";
}
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:self.titleStringToDisplay forKey:#"title"];
self.notification.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:self.notification];
}
if ([self.currentRepeatLabel.text containsString:#"Mon"]) {
[components setWeekday:1];
self.notification = [[UILocalNotification alloc] init];
self.notification.fireDate = [calendar dateFromComponents:components];
self.notification.repeatInterval = NSCalendarUnitDay;
[self.notification setAlertBody:[NSString stringWithFormat:#"A friendly reminder: %#", self.titleStringToDisplay]];
if (self.soundSwitch.isOn == YES) {
self.notification.soundName = #"soundeffect.wav";
}
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:self.titleStringToDisplay forKey:#"title"];
self.notification.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:self.notification];
}
// I'm doing this for each day.
NSCalendarUnitDay is repeating my notification everyday, not matter what days I have selected. I have tried NSCalendarUnitWeekOfYear but my notifications never fire when using that.
The goal is for the user to set their title, time, and repeat days (much like the native alarm app).
How do I set the repeat interval for this?
Update and new issue:
I am using NSCalendarUnitWeekOfYear now.
Here's my issue ... the notification is no longer firing now and the repeatInterval is always set to Monday instead of the day of the week that it steps through in code.
There are several ways to do that . You can use NScalender to find the weekday and than calculate that date on which you want to be notifications fire.
-(void) calculateweeknumber
{
NSCalendar *numberCalendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [numberCalendar components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
[comps setSecond:0.0];
weekday = [comps weekday];
NSLog(#"number is %d",weekday);
}
Your code has many problems.
Your date comes from a UIDatePicker obviously. You should use this date. Converting it back and forth is nonsense.
Also, get rid of all that duplicated code. It makes understanding (and fixing) the code more difficult. It hides the logic.
The real problem though is that you just set date components as if it were a date. It is not. You are starting with some date thrown into components, and then setting a weekday. This will not give another valid date - why should it? How could it? What should it adjust? The day? The month? The year? Into the future or into the past? The weekday seems to be simply ignored when converting to a date.
You need to calculate a proper date (this answer describes calculating the next monday).
I'm scheduling two notifications as shown below. The app is a long-lived app. One local notification is scheduled to run every hour. The other is scheduled to run once per day. Only the second scheduled notification (the hourly notifcation) fires.
- (void)scheduleNotification
{
LogInfo(#"IN scheduleNotification - DELETEYESTERDAY NOTIFICATION SCHEDULED.");
UILocalNotification *notif = [[UILocalNotification alloc] init];
NSDictionary *deleteDict = [NSDictionary dictionaryWithObject:#"DeleteYesterday"
forKey:#"DeleteYesterday"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
[components setDay: day];
[components setMonth: month];
[components setYear: year];
[components setHour: 00];
[components setMinute: 45];
[components setSecond: 0];
[calendar setTimeZone: [NSTimeZone systemTimeZone]];
NSDate *dateToFire = [calendar dateFromComponents:components];
notif.fireDate = dateToFire;
notif.timeZone = [NSTimeZone systemTimeZone];
notif.repeatInterval = NSDayCalendarUnit;
notif.userInfo = deleteDict;
[[UIApplication sharedApplication] scheduleLocalNotification:notif];
}
and then I schedule this after above:
- (void)scheduleHeartBeat
{
LogInfo(#"IN scheduleHeartBeat - HEARTBEAT NOTIFICATION SCHEDULED.");
UILocalNotification *heartbeat = [[UILocalNotification alloc] init];
NSDictionary *heartbeatDict = [NSDictionary dictionaryWithObject:#"HeartBeat"
forKey:#"HeartBeat"];
heartbeat.userInfo = heartbeatDict;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
[components setDay: day];
[components setMonth: month];
[components setYear: year];
[components setHour: 00];
[components setMinute: 50];
[components setSecond: 0];
[calendar setTimeZone: [NSTimeZone systemTimeZone]];
NSDate *dateToFire = [calendar dateFromComponents:components];
heartbeat.fireDate = dateToFire;
heartbeat.timeZone = [NSTimeZone systemTimeZone];
heartbeat.repeatInterval = NSHourCalendarUnit;
[[UIApplication sharedApplication] scheduleLocalNotification:heartbeat];
}
The above are scheduled when the app launches in the viewDidLoad of the main view controller.
- (void)viewDidLoad
{
[self scheduleNotification];
[self scheduleHeartBeat];
[super viewDidLoad];
//OTHER CODE HERE
}
Then in the appdelegate I have the following:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
LogInfo(#"IN didReceiveLocalNotification NOTIFICATION RECEIVED.");
NSString *notificationHeartBeat = nil;
NSString *notificationDeleteYesterday = nil;
application.applicationIconBadgeNumber = 0;
if (notification) {
notificationHeartBeat = [notification.userInfo objectForKey:#"HeartBeat"];
notificationDeleteYesterday = [notification.userInfo objectForKey:#"DeleteYesterday"];
LogInfo(#"IN didReceiveLocalNotification HEARTBEAT NOTIFICATION TYPE: %#", notificationHeartBeat);
LogInfo(#"IN didReceiveLocalNotification DELETEYESTERDAY NOTIFICATION TYPE: %#", notificationDeleteYesterday);
}
if ([notificationHeartBeat isEqualToString:#"HeartBeat"]) {
//CREATE THE HEARTBEAT
LogInfo(#"CREATING THE HEARTBEAT.");
//CALL THE FUNCTIONALITY HERE THAT CREATES HEARTBEAT.
}
if ([notificationDeleteYesterday isEqualToString:#"DeleteYesterday"]) {
//DELETE YESTERDAYS RECORDS
LogInfo(#"DELETING YESTERDAYS RECORDS.");
}
}
The notification that is scheduled last (scheduleHeartBeat) is the only notification that is fired.
Could somebody help me figure out why this is happening?
You have specified your repeat interval to NSDayCalendarUnit. So, your notification will be fire but at next day at specified time.
For testing purpose change your this repeat interval and check your code is working properly.
I have tested. Your code is working properly here.
I have implemented local notifications in a long-lived app. The app runs 24 hours per day in kiosk mode. One of the local notifications fires once per day and the other fires once per hour. The notification that fires once per day deletes all local core data information from the previous day. The notification that fires once per hour is a "heartbeat" for the app and creates a check in on the server once per hour.
Here is the schedule for the hourly heartbeat (it is in my main viewcontroller):
- (void)scheduleHeartBeat
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *heartbeat = [[UILocalNotification alloc] init];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
[components setDay: day];
[components setMonth: month];
[components setYear: year];
[components setHour: 00];
[components setMinute: 10];
[components setSecond: 0];
[calendar setTimeZone: [NSTimeZone systemTimeZone]];
NSDate *dateToFire = [calendar dateFromComponents:components];
heartbeat.fireDate = dateToFire;
heartbeat.timeZone = [NSTimeZone systemTimeZone];
heartbeat.repeatInterval = NSHourCalendarUnit;
[[UIApplication sharedApplication] scheduleLocalNotification:heartbeat];
}
I call the above method in viewDidLoad.
Then in my AppDelegate I have the following:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
application.applicationIconBadgeNumber = 0;
[[OLEngine myEngine] deleteStuffFromYesterday:#"MyObject"];
}
In the didReceiveLocalNotification I need to distinguish between which Local Notification has been fired because I do not want to call the method deleteStuffFromYesterday every hour - only once per day.
How can I distinguish between these scheduled local notifications in my app delegate code?
You can use the userInfo Property to store an NSDictionary for this.
Store the info:
localNotification.userInfo = #{ #"myCustomType" : #"heartbeat" };
Retrieve the info:
NSString *myCustomType = localNotification.userInfo[#"myCustomType"];
I am trying to implement local notification
This is what I have set
// Current date
NSDate *date = [NSDate date];
// Add one minute to the current time
NSDate *dateToFire = [date dateByAddingTimeInterval:20];
// Set the fire date/time
[localNotification setFireDate:dateToFire];
[localNotification setTimeZone:[NSTimeZone defaultTimeZone]];
Instead of 20, I want to put a fixed time(daily)to start push.
For ex:I want to push notification pop up at every 6:00AM.
How can do that ?
Thanks
You just need to properly create a NSDate object to be your fire date (time). Instead of using [NSDate dateByAddingTimeInterval: 20], use something like this:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay: 3];
[components setMonth: 7];
[components setYear: 2012];
[components setHour: 6];
[components setMinute: 0];
[components setSecond: 0];
[calendar setTimeZone: [NSTimeZone defaultTimeZone]];
NSDate *dateToFire = [calendar dateFromComponents:components];
Here are the Apple NSDateComponents API docs
And then when you add the date to the notification, set the repeat interval to one day:
[localNotification setFireDate: dateToFire];
[localNotification setTimeZone: [NSTimeZone defaultTimeZone]];
[localNotification setRepeatInterval: kCFCalendarUnitDay];
As with all date related code, make sure to test how this works during the switch to daylight savings time, if your time zone uses daylight savings time.
I guess what you need is NSDayCalendarUnit.
You can check this answer. And here is another tutorial worth reading.
NSDate *alertTime = [[NSDate date] dateByAddingTimeInterval:10];
UIApplication* app = [UIApplication sharedApplication];
UILocalNotification* notifyAlarm = [[[UILocalNotification alloc] init] autorelease];
if (notifyAlarm)
{
notifyAlarm.fireDate = alertTime;
notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
notifyAlarm.repeatInterval = 0;
notifyAlarm.soundName = #"Glass.aiff";
notifyAlarm.alertBody = #"Staff meeting in 30 minutes";
[app scheduleLocalNotification:notifyAlarm];
}