Setting Local Notifications on a new thread? - iphone

I need to know if it is possible to create a new thread to handle setting local notifications.
My app depends heavily on these notifications, so I want to make the app work while the phone sets the notifications.
Example:
(now)
you launch the app, the app hangs at the splash screen to set the local notifications, then it launches.
(I want)
The app launches and is usable while the Local notifications are set.
I need some sample code, too, please :)
(for the record, i am setting 60 local notifications each time the app enters foreground for my own reasons...)
Thanks!!

Yes this can be done, I do it all the time:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add the navigation controller's view to the window and display.
[NSThread detachNewThreadSelector:#selector(scheduleLocalNotifications) toTarget:self withObject:nil];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
-(void) scheduleLocalNotifications
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i = 0; i < 60; i++)
{
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
NSDate *sleepDate = [[NSDate date] dateByAddingTimeInterval:i * 60];
NSLog(#"Sleepdate is: %#", sleepDate);
localNotif.fireDate = sleepDate;
NSLog(#"fireDate is %#",localNotif.fireDate);
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(#"This is local notification %i"), i];
localNotif.alertAction = NSLocalizedString(#"View Details", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
NSLog(#"scheduledLocalNotifications are %#", [[UIApplication sharedApplication] scheduledLocalNotifications]);
[localNotif release];
}
[pool release];
}
Taken from a project I am working on now, I can confirm that It works as expected.
EDIT:
Example was leaking in scheduleLocalNotifications because handling the NSAutoreleasePool was missing – now it's added to the example.

One way to do threads is with is with performSelectorInBackground.
For example:
[myObj performSelectorInBackground:#selector(doSomething) withObject:nil];
You should note, however, that Apple is pretty strongly recommending that you use higher-level concepts like NSOperations and Dispatch Queues instead of explicitly spawning threads. See the Concurrency Programming Guide

Related

How would I make my timer run in the background and then send a local notification?

For some reason my timer only runs in the foreground. I've searched here for solutions but I couldn't find any good ones. My timer uses Core Data and my timeInterval is saved after every decrement. Also, I'm sending a UILocalNotification but that doesn't work. I'm assuming it doesn't send a alert view if its in the foreground..Well this is what I have now:
-(IBAction)startTimer:(id)sender{
if (timer == nil) {
[startButton setTitle:#"Pause" forState:UIControlStateNormal];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
} else {
[startButton setTitle:#"Resume" forState:UIControlStateNormal];
[timer invalidate];
timer = nil;
}
}
-(void)timerAction:(NSTimer *)t
{
if(testTask.timeInterval == 0)
{
if (self.timer)
{
[self timerExpired];
[self.timer invalidate];
self.timer = nil;
}
}
else
{
testTask.timeInterval--;
NSError *error;
if (![self.context save:&error]) {
NSLog(#"couldn't save: %#", [error localizedDescription]);
}
}
NSUInteger seconds = (NSUInteger)round(testTask.timeInterval);
NSString *string = [NSString stringWithFormat:#"%02u:%02u:%02u",
seconds / 3600, (seconds / 60) % 60, seconds % 60];
timerLabel.text = string;
NSLog(#"%f", testTask.timeInterval);
}
-(void)timerExpired{
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = #"Time is up";
localNotification.alertAction = #"Ok";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication]presentLocalNotificationNow:localNotification];
}
I would really appreciate some guidance to making my timer work in the background (just like Apple's timer). I'm not sure how I would use NSDateComponents since I also need the testTask.timeInterval to be updated even when the application in the background..
UIBackgroundTaskIdentifier bgTask =0;
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerCountDown:)
userInfo:nil
repeats:YES];
Now use this method to fire your notification. The timer will run in background.
As Josh Caswell wrote, you should save your timer state before your app goes in the background and then retrieve it when your app comes in the foreground again.
If you need to send a local notification at the right moment you can set it this way before entering the background, using your timer's state:
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyyMMddHHmmss"];
NSDate *when = [dateFormat dateFromString:desiredTime];
// desiredTime -> time you want your local notification to be fired, take this from your timer before app enters the background
[dateFormat release];
localNotification.fireDate = when;
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.alertBody = #"Hey";
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
[localNotification release];
EDIT: In your appDelegate you should find these methods:
(1) - (void)applicationWillResignActive:(UIApplication *)application
(2) - (void)applicationDidEnterBackground:(UIApplication *)application
(3) - (void)applicationWillEnterForeground:(UIApplication *)application
(4) - (void)applicationDidBecomeActive:(UIApplication *)application
(5) - (void)applicationWillTerminate:(UIApplication *)application
These are implemented specifically to manage the behavior of your app when its state changes.
There you can do everything needed before entering the background and when resumed.
Before entering the background you have some seconds to perform local notification schedule and save your timer state (using for example NSUserDefaults).
After that Apple will probably kill your app, if it doesn't try to work in the background for one of these reasons:
Implementing Long-Running Background Tasks For tasks that require more
execution time to implement,you must request specific permissions to
run them in the background without their being suspended. In iOS, only
specific app types are allowed to run in the background:
Apps that play audible content to the user while in the background,
such as a music player app
Apps that keep users informed of their location at all times, such as a navigation app
Apps that support Voice over Internet Protocol (VoIP)
Newsstand apps that need to download and process new content
Apps that receive regular updates from external accessories
Taken from here.
I suggest you read this, about background tasks.

iOS Alarm Clock

I have created a simple alarm notification App through which I can get real time, set alarm on or off, and play a single tone audio. But I need to play a sound which should start with a class VOID.
Below is the code:
To get and start alarm notification:
- (void)viewDidLoad {
[super viewDidLoad];
dateTimerPicker.date = [NSDate date];
}
- (void)presentMessage:(NSString *)message {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Hello!"
message:message
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
}
- (void)scheduleLocalNotificationWithDate:(NSDate *)fireDate {
UILocalNotification *notification = [[UILocalNotification alloc]init];
notification.fireDate = fireDate;
notification.alertBody = #"Time to wake up!!";
notification.soundName = #"PhoneOld.mp3";
[self playPause];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
- (IBAction)alarmSetOn:(id)sender{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.timeZone = [NSTimeZone defaultTimeZone];
dateFormatter.timeStyle = NSDateFormatterShortStyle;
dateFormatter.dateStyle = NSDateFormatterShortStyle;
NSString *dateTimeString = [dateFormatter stringFromDate:dateTimerPicker.date];
NSLog(#"Alarm Set: %#", dateTimeString);
[self scheduleLocalNotificationWithDate:dateTimerPicker.date];
[self presentMessage:#"Alarm ON!"];
}
- (IBAction)alarmSetOff:(id)sender {
NSLog(#"Alarm Off");
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[self presentMessage:#"Alarm OFF!"];
}
This is my VOID:
- (void)playPause {
RADAppDelegate *appDelegate = (RADAppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.radiosound == 0){
[appDelegate.radiosound play];
} else {
[appDelegate.radiosound pause];
}
}
How can I set the alarm to start playing the radiosound if is rated 0, like a:
notification.soundName = [self playPause];
But I know this is a NSString.
You don't need to assign a sound name to scheduled notification, just invoke the playPause method and get the name of sound file from notification, as shown below and just assign it to NSString and set property to it in appDelegate and access it to play that file.
AppDelegate.h
#property (nonatomic,strong) NSString *nsStr_soundFile;
AppDelegate.m
#synthesize nsStr_soundFile;
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification {
//Give call to play sound method.
self.nsStr_soundFile=notification.soundName;
VOID *obj=[VOID alloc]init];
[obj playPause];
}
You can make a trick with opting out of iOS multitasking by setting in your app .plist file this key UIApplicationExitsOnSuspend to YES as written here
When an app opts out, it cycles between the not-running, inactive, and
active states and never enters the background or suspended states.
An app that runs in the pre-multitasking compatibility mode keeps
running when the user locks the device while the app is in the
foreground. All the app has to do is wait for the alarm time and
execute its custom code.

UIApplication -scheduleLocalNotification: very slow when called by UIApplication -appWillTerminate:

I set the "application does not run in background" in my info.plist, so when user tap home button, app quits.
When my [UIApplication -appWillTerminate:] called, I will schedule 64 local notifications to system, all of them are non-repeating.
but that take a seemingly long time(6.17 seconds) on a iPhone4 with iOS6.0.1.
When I look at the time profiler, I found that the curve is very strange, it don't take much CPU time, but it do take a lot of time.
Also when I look at the call tree, 93% of the time is spent on [UIApplication -scheduleLocalNotification:] in the time range showed in the image.
Why?
This is how I generate my notifications:
UILocalNotification *n = [[[UILocalNotification] alloc] init] autorelease];
n.alertBody = #"some body";
n.hasAction = YES;
n.alertAction = #"some action";
n.fireDate = #"some date";
n.repeatInterval = 0;
n.soundName = #"my sound"
n.userInfo = aDictionaryWithAStringAbount10CharacterLongAnd2NSNumber.
[self.notifications addObject:n];
This is how I schedule my notifications:
-(void)endProxyAndWriteToSystemLocalNotification
{
_proxying = NO;
NSDate *dateAnchor = [NSDate date];
NSEnumerator *enumerator = [self.notifications objectEnumerator];
NSInteger i = 0;
while (i < maxLocalNotifCount) {
UILocalNotification *n = [enumerator nextObject];
if (!d) {
break;
}
if ([n.fireDate timeIntervalSinceDate:dateAnchor] >= 0) {
[[UIApplication sharedApplication] scheduleLocalNotification:n];
i++;
}
}
[self.notificationDatas removeAllObjects];
}
This would help:
-(void)endProxyAndWriteToSystemLocalNotification {
[[UIApplication sharedApplication] setScheduledLocalNotifications:self.notifications];
}
iOS 4.2 and later
read UIApplication Class Reference for detailed description
I think the problem is that you are trying to schedule 64 local notifications. Is there a reason to do all of these on app termination? Apples scheduleLocalNotification was not designed to be called so many times on termination

How to initialize local notification?

I want to implement local notification in my clock app.Basically i want that a music file should be played after every half an hour like in ship's clock in which chimes are played after every 30 minutes.
Can anyone give rough idea as how i can implement this functionality even when the app enters in background?
I recently used the Local notification stuff and used the following functions
//Setting up the Local Notifications
for (int i= 1 ; i<=10; i++) { //We here set 10 Notification after every 30 minutes from now you can modify it accordingly
NSDate *scheduled = [[NSDate date] dateByAddingTimeInterval:60*30*i]; //These are seconds
NSDictionary* dataDict = [NSDictionary dictionaryWithObjectsAndKeys:scheduled,FIRE_TIME_KEY,#"Background Notification received",NOTIFICATION_MESSAGE_KEY,nil];
[self scheduleNotificationWithItem:dataDict];
}
Where scheduleNotificationWithItem is defined as
- (void)scheduleNotificationWithItem:(NSDictionary*)item {
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
if (localNotification == nil) return;
localNotification.fireDate = [item valueForKey:FIRE_TIME_KEY];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.alertBody = [NSString stringWithFormat:NSLocalizedString(#"%#", nil), [item valueForKey:NOTIFICATION_MESSAGE_KEY]];
localNotification.alertAction = NSLocalizedString(#"View Details", nil);
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.userInfo = item;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
[localNotification release];
}
Finally you can handle these notifications as
You can handle these notifications as follows
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// Do the required work you can obtain additional Info via notification.userInfo which happens to be a dictionary
}
reading the developer documentation will help you more to understand the stuff.Hope it helps
You can use UILocalNotifications and set their 'firedate', according to your requirement and then schedule the notification. These notifications doesn't bother whether your app is running or is in background they will always show up like an alertview.

how to set uilocalnotification firedate for a fixed date?

i want to set localnotification say for a program which starts at 6:00 pm.For that i have taken the time in a date variable and i am comparing it with current date from system.
Say setDate is for fixed date i.e 6.00 pm so i have to set firedate such that it shows the notification before 30 mintes the program starts. The examples i have seen in that the firedate is set according to currentdate.
Can someone tell me how can i set firedate according to my fixed date??
You fire the local notification this way
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
return;
localNotif.fireDate = [NSDate date];// Now here you can manage the fire time.
localNotif.timeZone = [NSTimeZone defaultTimeZone];
// Notification details
localNotif.alertBody = #"BusBuddy";
// Set the action button
localNotif.alertAction = #"View";
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
// Specify custom data for the notification
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:#"You are near to reach the Bus Stop" forKey:#"someKey"];
localNotif.userInfo = infoDict;
// Schedule the notification
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
This line of code will work for you. you just need to provide the date time for this
localNotif.fireDate = [NSDate date];
And now for formatting your date time you can refer to these links
iphonedevelopertips.com
developer.apple.com, CFDatesAndTimes
developer.apple.com, DataFormatting
Well then you can handle your local notification in application delegate file when ever you get the notification.
e.g. here is the delegate method which is fired everytime when you get the local notification.
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
//Handle the notificaton when the app is running
NSLog(#"Recieved Notification %#",notif);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Hey Neha" message:#"Sanjay wants to be your friend " delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
[alertView release];
SystemSoundID bell;
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"WhoopFlp" ofType:#"wav"]], &bell);
AudioServicesPlaySystemSound (bell);
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
i=0;
}
}
So what I am doing here is simply showing the alert and playing a system sound when my local notification occurs.
So now what you want is to navigate to the page in the program where you were when you get the local notification.So simply in this delegate method you need to allocate your view controller and need to push the view controller to that view where you want to be.
That would solve your problem.
Well i had a similar kind of problem what i wanted is to show the notification in the background and in the front end as well, so writing the 2 different methods in my app was not worthful.so i handled it this way in the delegate method which will show the notification in the front end as well.
Good luck to you.
localnotification =[[UILocalNotification alloc]init];
[localnotification setFireDate:[NSDate dateWithTimeIntervalSinceNow:[lodatepicker countDownDuration]]];
[localnotification setAlertAction:#"Launch"];
[localnotification setHasAction: YES];
[localnotification setAlertBody:[lotextview text]];
// [localnotification setSoundName:musicString];
localnotification.timeZone = [NSTimeZone defaultTimeZone];
[localnotification setApplicationIconBadgeNumber:[[UIApplication sharedApplication] applicationIconBadgeNumber]+1];
[[UIApplication sharedApplication] scheduleLocalNotification:localnotification];