Background
I am in the early stages of learning iOS Development (and Objective C). After the latest tutorial I followed, I decided to take the lesson a little further.
After the tutorial, I had an Alarm Clock application that fired a LocalNotification at a time set by the user. Since the local notification only played the alarm sound while the app was in background mode, I didn't think it was much of an alarm clock. So I set out to add alarm functionality while the app was open and in focus.
Where I am now
My alarm clock application functions exactly as it should. If the app is open, the user receives a UIAlertView and the alarm sound.
The Problem
To trigger the UIAlertView, I am using the didReceiveLocalNotification: method.
There is a fairly big delay between the time set by the user and when didReceiveLocalNotification: is called. 38 seconds to be exact.
I may just be nitpicking, but it's killing me that I can't figure out why. I went out searching through GitHub to find some other examples, and they all seem to be following the same pattern as I am.
What might cause a delay like this? If this is normal behavior, what can be done to get rid of the delay?
Please let me know if any of my code might be helpful. As didReceiveLocalNotification: is a predefined method, and I have no control over when it is called, I'm not sure what code you might need.
Additional Information
Method for setting alarm time
- (void) scheduleLocalNotificationWithDate: (NSDate *) fireDate
{
UILocalNotification *backgroundNotification = [[UILocalNotification alloc] init];
backgroundNotification.fireDate = fireDate;
backgroundNotification.alertBody = #"Wake Up!";
backgroundNotification.soundName = #"My_Alarm.mp3";
[[UIApplication sharedApplication] scheduleLocalNotification: backgroundNotification];
}
Related
I've had an app on the App Store for over a year now which uses UILocalNotifications to act as reminders. The UILocalNotifications use the repeatInterval, set to NSCalendarUnitMinute, to keep reminding the user of whatever they wanted to be reminded of.
Everything was fine with the app when building against iOS 7/8 but now that I've started to build it against iOS 9 I'm having epic issues using [[UIApplication sharedApplication] cancelLocalNotification: notification] to stop previously scheduled notifications.
I've checked the number of notifications in [[UIApplication sharedApplication] scheduledLocalNotifications] before and after cancelling a notification to verify that the count does get decremented, but when it comes round to the time that the notification would have hit its repeatInterval the notification cheerily pops up its head to haunt me...
I've tried all sorts of things... cancelling individual notifications, [[UIApplication sharedApplication] cancelAllLocalNotifications], [UIApplication sharedApplication].scheduledLocalNotifications = [NSArray array] but nothing was doing the trick at all...
A number of times I thought I'd solved the problem but then it would keep rearing its head, generally on older notifications I'd scheduled, though always seemed fine on ones I'd just recently setup.
Just now I managed to get rid of a lingering, un-cancellable notification by force quitting the app after an attempt to cancel it... which is hardly an elegant solution for end users other than myself...
Has anyone else had these kinds of issues with local notifications, possibly specifically those that are setup to auto-repeat?
For the sake of it I'll reference this similar post which could technically be called a duplicate but I'm posting this new question as the other one isn't getting any interest and needs my somewhat different situation to be described and solved! (PS if you get this question closed as a duplicate I will cry.. and no one wants to see that, right?)
I have an app which must report user location even if it backgrounded or even killed (terminated). The issue is that the app should report the location not rare than 1 hour interval.
I'm using significant location change (SLC) to track all movements which is quite enough when user is on the go but once user stops no further updates is raised and the app has no opportunity to submit new location (while user stays in the same area but without SLC).
To cover this case I start to use Background fetches to periodically send updates location in background even without SLC). The issue here is that background fetches used to work quite often (every 20-30 min since I used then in another iOS 7.x app) but now with iOS8 / iOS9 I can get it only once a day or so which is not acceptable in my case. I have executed ton of tests, developed simple background fetch app which add a local notification on fetch. No luck to force it to work more often.
Here is my AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
return YES;
}
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
NSDate *now = [NSDate date];
localNotification.fireDate = now;
localNotification.alertBody = [NSString stringWithFormat:#"Background fetch!"];
localNotification.soundName = UILocalNotificationDefaultSoundName;
NSInteger number = [UIApplication sharedApplication].applicationIconBadgeNumber;
number++;
localNotification.applicationIconBadgeNumber = number;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
completionHandler(UIBackgroundFetchResultNewData);
}
All what is done here is to add local notification on every background fetch. I always finalize the background execution with UIBackgroundFetchResultNewData.
Do you have any suggestions on how to force background fetches to work more often (or prove links that it is not possible any more)?
Any alternative solutions to meet my requirement are also welcomed!
It turned out that background fetches in iOS depend a lot on what you are doing within the handler, especially network activity. Here the list of dependencies which you should consider trying to understand if and how often iOS will execute your fetch:
time you spend in handler
result (NoData, NewData)
error handling (you will be launched less likely if you code crashes
timeouts (your code execution could be interrupted by iOS)
power usage
network activity related to result (you MUST do a network request when saying that you have NewData, otherwise your fetch could be executed next time in a day or so never.
Apps that download small amounts of content quickly, and accurately
reflect when they had content available to download, are more likely
to receive execution time in the future than apps that take a long
time to download their content or that claim content was available
but then do not download anything.
The last item turned out to be crucial in my case, because for testing purposes I declared fetches and never tried to download anything. Once I started to use network in the handler, background fetches continue to work as expected every 15-30 minutes.
Apple docs:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
I worked on a similar project. After trying various solutions, the only way I've found is the solution proposed here:
https://github.com/voyage11/Location
Here is the tutorial corresponding to the git:
http://mobileoop.com/getting-location-updates-for-ios-7-and-8-when-the-app-is-killedterminatedsuspended
All credits to https://stackoverflow.com/users/1995940/ricky
It's saved me a lot of time!
I'd like to show a (local) notification to the user when my app is terminated, be it by iOS itself or by the user via the task switcher.
When iOS kills the app because of memory pressure the applicationWillTerminate function is called on my AppDelegate, so I can schedule a local notification in that case.
However, when the user kills the app by swiping it to the top in the task switcher, the applicationWillTerminate function is not called (as stated in the Apple docs and various answers here on SO). And yet, there are apps that still do succeed in showing a (local) notification to the user in that case (especially fitness tracking apps, e.g. Human), asking the user to restart the app so the background tracking can continue.
I can think of some (mostly awkward, or at least battery consuming) ways to get this done, but is there a nice way to show such a notification to the user? Especially to do so almost instantly after the app is killed by the user, which excludes a lot of possible workarounds with scheduling and cancelling local notifications every n seconds in the background ...
Found it ... apparently the apps that show a notification like the one I want to show do it by using background location updates to (re)schedule a local notification so it will never show up, and once they are killed by the user the notification stays active and fires.
Doesn't sound that nice, but probably it's the only way to do it apart from pinging the app from the server periodically.
It would be nice to have a more decent (and energy-efficient) way to do this, e.g. by consistently getting the time to do something just before an app is terminated, no matter the reason for termination.
I tried like this:
Must enable background mode (bluetooth, voip, location service)
Add this code in didFinishLaunchingWithOptions:
[self addLocalNotification];
[NSTimer scheduledTimerWithTimeInterval:9.0f
target:self
selector:#selector(addLocalNotification)
userInfo:nil
repeats:YES];
in addLocalNotification
- (void) addLocalNotification{
NSDate * theDate = [[NSDate date] dateByAddingTimeInterval:10]; // set a localnotificaiton for 10 seconds
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];
// Clear out the old notification before scheduling a new one.
if ([oldNotifications count] > 0)
[app cancelAllLocalNotifications];
// Create a new notification.
UILocalNotification* alarm = [[UILocalNotification alloc] init];
if (alarm)
{
alarm.fireDate = theDate;
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = 0;
alarm.alertBody =#"App must run" ;
[app scheduleLocalNotification:alarm];
}}
And it works for me. addLocalNotification will run as long as app run/background. Once it is Terminated already scheduled local notification will fire on time. Time interval we can change on our interest.
We are making an alarm clock app and want to have our UILocalNotification sound be an alarm noise. Currently we can only get the UILocalNotificationDefaultSound to work for us.
We are trying to do something like this:
localNotification.soundName = "iphonesongw.wav"
Where iphonesongw.wav is in our project.
Here is what I've read from Apple's Doc:
Sounds that last longer than 30 seconds are not supported. If you specify a file with a sound that plays over 30 seconds, the default sound is played instead.
I keep reading the documentation and is just not making sense. So i decided to ask the question. Applications on the Iphone like the clock which has an alarm on it. When i set the alarm and close the application the application still notify's me at 4:30am eventhough i have closed the application. Now my understanding is that in order for something like this to work you would have to have it running on the background at all times. This is the part that does not make sense. If i completely shut down the app by double clickin my home button and then swiping up to get rid of the app, how does the app monitor time if is no longer running in the background? Apple states that their are 5 states
1-NOT RUNNING
2-Inactive
3-Active
4-Background
5-Suspended
explanation of the above are located here
i would imagine that when i shut down an application the state is not running. However the alarm application still comes on. HOW? this is killing me. Is there a special state that only native apps can have?
Any information or further understanding would be greatly appreciated.
This is not the case Miguel. What you are looking for is:
https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/Reference/Reference.html
Using UILocalNotifications, your app does not have to keep track of time at all. It schedules a notification, and the operating system keeps track of it. I have built quite a few apps with notifications/alarms. I hope this is helpful.
You can use Local notifications.
iOS will notify user when your previously added local notification fires. [iOS will add notification but your app won't be running at all. When user will tap on the notification then the app will open.]
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UILocalNotification *notification = [[UILocalNotification alloc]init];
notification.repeatInterval = NSDayCalendarUnit;
[notification setAlertBody:#"Hello world"];
[notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:1]];
[notification setTimeZone:[NSTimeZone defaultTimeZone]];
[application setScheduledLocalNotifications:[NSArray arrayWithObject:notification]];
}
Tutorial:
http://www.appcoda.com/ios-programming-local-notification-tutorial/
http://www.icodeblog.com/2010/07/29/iphone-programming-tutorial-local-notifications/
Concept guide:
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Introduction.html