UILocalNotification does not fire after 10 minutes in background - iphone

I have a VoIP application. Which is working fine. Call is working in foreground and background.
Following steps are done:
UIBackgroundModes => voip in Info.plist
Configured one of the app’s sockets for VoIP usage.
Before moving to the background, setKeepAliveTimeout:handler: is called
Configured audio session to handle transitions to and from active use.
To ensure a better user experience on iPhone, used the Core Telephony framework to adjust behavior in relation to cell-based phone calls;
To ensure good performance for VoIP app, used the System Configuration framework to detect network changes and allow app to sleep as much as possible.
Now the thing is when application is in background and a call comes then UILocalNotification fires for the following. And user can see a notification with two buttons CANCEL and RECEIVE
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask: bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if ([friend length]>0) {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat: NSLocalizedString(#"%#", nil), friend];
localNotif.alertAction = NSLocalizedString(#"Receive", nil);
localNotif.soundName = #"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
friend = nil;
}
}
sleep(1);
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
});
}
- (NSString *) checkForIncomingChat {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *incall = [prefs objectForKey:#"incall"];
if ([incall length]>0) {
[prefs setObject:#"" forKey:#"incall"];
return incall;
}
return #"";
};
Now the problem is:
After going to the background by pressing home button application fires UILocalNotification if any call comes within 10 minutes.
After 10 minutes if any call comes it is running in the background. UILocalNotification does not fire, so user can not know anything.
It happens because background task stops after 10 minutes.
How can I manage it or extend background task for long running or restart background task.
More and more answer I have found after searching but nothing works for long running background task.
Please anybody help me. I am trying it since 2 weeks.

It sounds like you have a VoIP socket that you are receiving the call on, so rather than looping and polling for call state, you can just present the local notification at the point that you read the data off the socket.
If the VoIP control socket is TCP, and marked with the appropriate ...NetworkServiceTypeVoIP key, your app will automatically get woken up from suspension for 10 seconds, at which point you can present the local notification.
See Configuring Sockets for VoIP Usage for more information.
Once that is done, all of the code that you shared above can be removed.

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.

scheduleLocalNotification doesn't work for jailbreak apps?

I have an app that calls scheduleLocalNotification, but it doesn't work when I install it to /Applications instead of /var/mobile/Applications:
- (void) doNotify
{
// this doesn't work when app is in /Applications but does in /var/mobile/Applications
UILocalNotification * theNotification = [[UILocalNotification alloc] init];
theNotification.alertBody = #"Finished processing.";
theNotification.alertAction = #"Ok";
theNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
[[UIApplication sharedApplication] scheduleLocalNotification:theNotification];
NSLog(#"notification scheduled: %#", theNotification);
}
I tried presentLocalNotification instead in case it was a timing issue.
I implemented didReceiveLocalNotification in app delegate to see if that was being called instead, but it wasn't, it was only called when app is in foreground like it should.
If I put the app back in /var/mobile/Applications, it works as it should.
I'm using Xcode 4.2.1 and running iOS 5.1.1 on an iPhone 4S and iPod Touch 4g
EDIT: App can run in the background because it is a music app
I met the same problem just now. And I already solve this problem through the official document.
In fact, you should regist the notification to get access to use notification:
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes: UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil];
[[UIApplication shareApplication] registerUserNotificationSettings: settings];
Register for Notifications
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[application registerUserNotificationSettings:[UIUserNotificationSettings
settingsForTypes:UIUserNotificationTypeAlert| UIUserNotificationTypeSound | UIUserNotificationTypeBadge categories:nil]];
}
[launchOptions valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];
}
Schedule Notification
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(#"startLocalNotification");
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:2];
notification.alertBody = #"notification Message ";
notification.timeZone = [NSTimeZone defaultTimeZone];
notification.soundName = UILocalNotificationDefaultSoundName;
//notification.applicationIconBadgeNumber = 10;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}

DidReceivedLocalNotificaiton not called when localNotification.alertBody is commented

I have following line of code to generate local notification after every 1 minute (For Testing), It works perfectly in following condition.
When application is in running state.
When application is in background state and and localNotification.alertBody = #"Local Notification Received" is not commented.
Now when i comment the "localNotification.alertBody" it works fine when application is in running state but didn't work when application is in background state.
Following is the line of code to create local notification.
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
[localNotification setRepeatInterval:NSMinuteCalendarUnit];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.repeatCalendar = [NSCalendar currentCalendar];
localNotification.fireDate = fireDateOfNotification;
//localNotification.alertBody = #"Local Notification Received";
localNotification.repeatInterval = NSMinuteCalendarUnit;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
[localNotification release];
in - (void)applicationDidEnterBackground:(UIApplication *)application
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIBackgroundTaskInvalid;
It seems that you are trying to execute code in the background, without showing a notification to the user.
By definition, UILocalNotification will not display a popup if alertBody is nil, even though didReceiveLocalNotification will be called PROVIDED THAT THE APPLICATION IS RUNNING.
If the application is in the background and alertBody is empty, then no popup alert and no execution of any code.
From the API documentation:
alertBody
...
Assign a string or, preferably, a localized-string key (using NSLocalizedString) as the value of the message. If the value of this property is non-nil, an alert is displayed. The default value is nil (no alert).
Are you sure that application:didReceiveLocalNotification: has not been called at all? Or is it just the alert you are missing?

About Push Notification

I am developing an app ,where I need to provide the notification to the user, when he is say x meter from the location.I have implemented the map,the logic for all that.Now I want to implement the push notification in that.I have read here on the stack overflow but didn't find the solution.I knew that I need to provide the methods in the app delegate.But i want to know how my server will come to know that the user is x meters from the destination.Do I also need to parse the webservice and send the data in the didupdatetoUserLocation.Or the server will take care of that.If server would that then how the server will come to know.
Any help would be appreciated
Thanks All......
you would have to send all the location-information to your server and your server would have to calculate, whether the push service shall become active or not. That would just work, if the application is active in the background and would cause a lot of communication stack.
I would suggest to use the local push service. By calling a function in the background on the device you can track the location and compare it to the "push"-location. If the push shall be shown, continue with UILocalNotification which is available since iOS 4:
http://developer.apple.com/library/ios/#documentation/iphone/Reference/UILocalNotification_Class/Reference/Reference.html
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil)
{
return;
}
localNotif.fireDate = [NSDate date];
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(#"Hey there, you are here: %#\n%#", nil), #"Country", #"Town"];
localNotif.alertAction = NSLocalizedString(#"W00t show me", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1344] forKey:#"LocationPush"];
localNotif.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
Since you need a server based push service, implement the following methods in your appdelegates method applicationDidFinishLaunching, thats how you can register the device for the service:
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound)];
Further on you can implement the following methods for detailed settings:
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
{
}
Since I'm not that familiar with server-based push notification I can't tell you more, all I know about is, that you need to set up a server with the key, that you generated and registered in the provision profile of apple:
http://developer.apple.com/ios/manage/bundles/index.action

UILocalNotification repeat sound

I have used the code from apples example from this page: Link, but I can't seem to get the sound to repeat. I have checked other applications, such as skype (for VOIP) and Alarm Clock Pro (audio?) but I cannot get the sound file to be repeated.
This is my code:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
AlarmHandler *AHinstance = getAlarmHandlerInstance();
UIApplication* app = [UIApplication sharedApplication];
NSArray *alarmList = [AHinstance getAlarms];
NSArray *oldNotifications = [app scheduledLocalNotifications];
if ([oldNotifications count] > 0)
{
[app cancelAllLocalNotifications];
}
for (Alarm *theAlarm in alarmList) {
NSDate *alarmDate = [theAlarm getNearestActivationDate];
Package *alarmPackage = [theAlarm getAlarmPackage];
NSArray *fileList = [alarmPackage getVoiceFileListForBackgroundNotificationWithHour:theAlarm.larmHour];
if( alarmDate == nil ) continue;
UILocalNotification* alarm = [[[UILocalNotification alloc] init] autorelease];
if (alarm)
{
NSLog(#"File: %#", [fileList objectAtIndex:0]);
alarm.fireDate = alarmDate;
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.soundName = [fileList objectAtIndex:0];
alarm.alertBody = #"Time to wake up!";
alarm.repeatInterval = 0;
[app scheduleLocalNotification:alarm];
}
}
}
Any suggestions on how I can fix this?
I have had suggestions to register app as audio player and play sounds in the background, but it seems that apple does take kindly to those applications because they aren't real audio players. Therefore they deny those apps.
Regards,
Paul Peelen
There is no way to do this for local notifications. You can either register as a VOIP app or as a "background audio" app, which have separate APIs. However, if you do not provide appropriate functionality to qualify for those kinds of uses, you'll most likely be rejected.
Yes this is possible, as the documentation states:
Your own applications can schedule up to 128 simultaneous notifications, any of which can be configured to repeat at a specified interval
You just need to configure the repeatInterval property:
The calendar interval at which to reschedule the notification.