UILocalNotification fires after reinstalling the app - iphone

My app has an alarm function using UILocalNotification, and it works great. However, if the user uninstalls the app, then later REINSTALLS it, he would receive all the "in between" notifications at once.
I have tried to call:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
if it's the first time the app is launched, but it doesn't help, because the notification is received even before application:didFinishLaunchingWithOptions: is called.
This was worse in 4.0 when the alarm was repeated even if the user has deleted the app, but at least that bug was fixed by Apple in later release. However now I'm stuck with this. Anyone has an idea?

According to Apple, this is not a bug (I filed a bug report). The system retains the UILocalNotifications for uninstalled apps for 24 hours just in case the user deleted the app by accident, and restores the said UILocalNotifications if the app is re-installed within that time frame.
The solution would be to remove all UILocalNotifications on first startup, like so:
- (BOOL) application: (UIApplication*) application
didFinishLaunchingWithOptions: (NSDictionary*) launchOptions
{
if (self.isFirstRun)
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
self.firstRun = NO;
}
/* Other code here */
...
}
of course, implement your own firstRun setter and getter to fetch/save into persistent storage, like NSUserDefaults.

This is actually a bug in iPhone. If you removed the application and install it later also, it will have same app id, so when the application is reinstalled all the past local notifications were fired even if you didn't open the app.

Related

How to turn Auto-Lock to Never in Swift?

Is there any Swift command to actually set the Auto-Lock to Never or a specific time period? I want to create a simple app that only has two buttons: one is to set the Auto-Lock to Never and the other one is to set it back to iOS default (1 min).
So when a user open this app and tap the Never button, s/he can open other apps but the iPhone or iPad will never auto lock while running the other apps. If s/he is done with other apps, s/he can open this app again and tap the Default button to set the Auto-Lock back to 1 min.
I understand this can be done from the Settings but I am just curious how I can do it from the backend using Swift.
I am new to Swift, btw.
Thanks much!
So when a user open this app and tap the Never button, s/he can open other apps but the iPhone or iPad will never auto lock while running the other apps
You can't do that. You are sandboxed. You cannot affect what happens to the user while running some other app.
When app is open, try to disable the idleTimer in viewDidLoad
UIApplication.shared.isIdleTimerDisabled = true
When app is closed to open other apps, try to -enable again idleTimer when yourViewController disappears, so put this in viewDidDisappear
UIApplication.shared.isIdleTimerDisabled = true
You need to make use of this API call to set the idle timer disable and enable.
This is in objective-C. Just convert it to swift. The API is available in UIApplication.h
-(void) onApplicationDidActivate:(NSNotification*) notification
{
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
}
-(void) onApplicationWillDeactivate
{
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
}

iOS app badge persists

I have an iPhone app which uses local notification. It is working almost perfectly, although I have a little problem: The red balloon (app badge) from notification doesn't dismiss when I launch the app. What can I do to fix it?
I've already deleted the app from iPhone, but when I compile it again with Xcode, I comes back again.
Try
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
after you have processed your notification.

applicationWillTerminate when is it called and when not

Hi I have read several questions on SO about applicationWillTerminate getting called and not getting called.
I wanted to summarize what I understood as there are several posts that speak differently.
For IOS (without multitasking) it is called always when home button is pressed.
For IOS 4 and above
a. it is not called when pressing home button (as the app moves to background)
b. it is called when closing the app from the multi tasking dock and if the app has a sudden terminate flag in info.plist disabled else it is not called. ( I set the "Application should get App Died events" and even then on closing the app from the multitasking dock the terminate function did not get called)
Based on that I had a couple of questions
Is it a good practise to set the Application should get App Died events flag? ( I set the "Application should get App Died events" and even then on closing the app from the multitasking dock the terminate function did not get called)
or
Is registering for "UIApplicationWillTerminateNotification" a better thing to do than the info.plist setting?
Basically I need to do some work only when the app terminates and NOT when it moves to background.
or
EDIT (1):
When the app is terminated the following is sent to the APP. How do I catch it?
Program received signal: “SIGKILL”.
EDIT (2):
Please note : It is not getting called in IOS 4 and above when removing from the multitasking dock. You might think it is. But in my case it is not.
I am asking if anyone knows why? Is there something else I am missing.
Also Note I set the "Application should get App Died events" and even then it is not getting called.
EDIT (3):
The answer for the following question also did not work.
applicationWillTerminate does not get invoked
Anybody facing the similar issue as me?
In short, unless you have UIApplicationExitsOnSuspend in your Info.plist set to YES, in iOS4 and above there is no guarantee that applicationWillTerminate: will ever get called.
As the documentation says:
For applications that support background execution, this method is
generally not called when the user quits the application because the
application simply moves to the background in that case. However, this
method may be called in situations where the application is running in
the background (not suspended) and the system needs to terminate it
for some reason
(Emphasis mine.)
If you need to do something before the app exits you need to do it in applicationDidEnterBackground:. There is no way to catch SIGKILL.
I see -applicationWillTerminate: getting called with the following test. In a new project (I used the 'Single View Application' template), add the following to the AppDelegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"%s", __PRETTY_FUNCTION__);
__block UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
if (identifier != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:identifier];
identifier = UIBackgroundTaskInvalid;
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
for (int i=0; i < 20; i++) {
NSLog(#"%d", i);
sleep(1);
}
if (identifier != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:identifier];
identifier = UIBackgroundTaskInvalid;
}
});
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
This example will start a background task when the app enters the background. The task is just a 20s delay (with logging once a second) that keeps the app running in the background (note the difference between running in the background and suspended) long enough to allow it to be killed from the app switcher.
So, to test it, run the app, hit the home button to send the app to the background, then before the 20s delay is up, remove the app from the app switcher. After the end of the 20s, -applicationWillTerminate: is called. You can watch the console in Xcode to verify that this is the case.
I tried this in the iOS Simulator for iOS 5.1 and 6.1 (both iPhone) and saw it happen in both cases. I also tested on iPhone 4S running iOS 6.1.2 and saw the same behavior.
As I know, there are 3 situations that your application will die.
Terminated by the end user, you can do something in -[UIApplication applicationWillEnterBackground:], in which case, -[UIApplication applicationWillTerminate:] will NOT be called.
Dropped by the system, such as memory not enough, you can do something in -[UIApplication applicationWillTerminate:], in which case, we do NOT know whether applicationWillEnterBackground: has been called;
Crashed, nothing can be done except using some kind of Crash Reporting Tool. (Edited: catching SIGKILL is impossible)
Source: http://www.cocos2d-iphone.org/forum/topic/7386
I copied my state saving code from applicationWillTerminate to applicationDidEnterBackground and also added a multitaskingEnabled boolean so that I only call state saving in applicationDidEnterBackground. BECAUSE, there is one instance on a multitasking device where applicationWillTerminate is called: If the app is in the foreground and you power off the device. In that case, both applicationDidEnterBackground and applicationWillTerminate get called.
As we know that the App has only 5 sec when -applicationWillTerminate being called. So If someone want to update the server at that point. Than use
Synchronous call.
[NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:&error];
Note:- -applicationWillTerminate will not call if app is being killed from suspended state. Suspended state means app is not working anything in backgroupd. One of the solution for this is to use background task.
Based on Andrew's test, I understand the docs for applicationWillTerminate(_:) to be meant as having the following clarifications:
For apps that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the app. For apps that support background execution, this method is generally not called [right away] when the user quits the app because the app simply moves to the background in that case. However, this method may be called [instead of beginBackgroundTask(expirationHandler:)] in situations where the app is running in the background (not suspended) and the system needs to terminate it for some reason.
// absolute answer applicationWillTerminate
func applicationWillTerminate(application: UIApplication) {
print("applicatoinWillTerminate")
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
step 1: command + shift + h (double click(tab))
step 2: move app top side (kill)
step 3: applicationWillTerminate Work

Schedule a local notification on firing a local notification

I want to schedule a local notification when my previous local notification gets fired.
It should get scheduled disregarding the user taps 'View' or 'Cancel'.I am not getting proper place(delegate method)to schedule a new notification.According to Apple docs,application:(UIApplication *)application didFinishLaunchingWithOptions: can be used but it doesn't seems to be get called when application comes to foreground from background and application:(UIApplication *)application didReceiveLocalNotification: gets called only on click of 'View' and not on close.How should I do this?Any help is highly appreciated.
Yes. You are correct about the local notifications. You should click "view" to get the didReceiveLocalNotification: triggered. If you click "Cancel", you are not caring about the notificaiton. If you don't care, why should the iOS care? :-)
You are scheduling the notification. So, you know when it will be fired. Don't you? Then why wait for the first notification to be fired? Just schedule the second notification along with the first notification.
A workaround:
Local and Push Notification Programming Guide
says that only 64 local notifications are allowed per app. So, schedule the first 64 notifications initially. And when the app opens the next time, check [UIApplication sharedApplication] scheduledLocalNotifications], and schedule the next (64 - scheduledLocalNotifications) notifications.
int scheduledNotifications = [UIApplication sharedApplication] scheduledLocalNotifications];
int n = 64 - scheduledNotifications;
[self Schedule-next-n-notifications];
Note: We can't guarantee that this will work perfectly. In case, if the app opens after very long gap, for example after 1 or 2 months, some notifications would have not been scheduled at the proper time.

iphone local notification in simulator

I just downloaded xcode and trying to make local notification example. The question is if local notification works in simulator?
thank you
Yes, local notifications work with the simulator. However, make sure you are implementing application:didreceiveLocalNotification in your app delegate if you want to see the notification while your app is in the foreground:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"MyAlertView"
message:notification.alertBody
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
if (alertView) {
[alertView release];
}
}
Otherwise, make sure you schedule your notification for some time in the future, then close the application, in order to see the Apple sample work:
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif == nil) return;
NSDate *fireTime = [[NSDate date] addTimeInterval:10]; // adds 10 secs
localNotif.fireDate = fireTime;
localNotif.alertBody = #"Alert!";
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
It's easy to think you're not implementing the test code correctly, and you just aren't handling the event while the app is running.
Another gotcha you might find, for anyone who stumbles on this older question: iOS 8 introduced new notification permissions; and your app has to explicitly ask for them.
In your AppDeligate.m:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//register local notifications
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
//the rest of your normal code
return YES;
}
If you don't, your notification will never fire, and you'll get a wonderful message like this in your logs:
"Attempting to schedule a local notification <UIConcreteLocalNotification: 0x7ae51b10>{... alert details ...} with an alert but haven't received permission from the user to display alerts"
local notifications work on simulator, push notifications do not
To test local notifications in iPhone simulator, follow these steps:
As simulator time is exactly the one in your machine, change the time of your machine to the 1-minute previous of your desired time (when you are expecting your local notification to fire)
Restart simulator(this is awkward, but sometimes it seems iPhone simulator fails to get updated time instantly)
Run the simulator once again(maybe by running your app from xcode, in which case you must press the home button to send your app to background). Once the time is reached you should see the notification
These steps helped me always to get successful local notifications.
App Settings Notification Section not showing
I ran into this issue as well. What seems to happen is the following,
Your app runs / is installed on simulator (might have to reset the simulator state of your app, see below).
Your app has not asked for permission to show notifications.
Therefore your app settings (Settings > App Name) won't show the notification section,
You have to request notifications in the app (using UNUserNotificationCenter.current().requestAuthorization etc).
Then your app will have a section for notifications (even if you deny the allow notification prompt)!
Resetting App State
After this I launched the app again and the notification setting still showed (and showed as Off since I denied permission).
To reset this and reproduce the original problem I had to do the following,
Enable simctl XCode command line utility (See How do I fix the xcrun unable to find simctl error? )
Run in terminal xcrun simctl uninstall booted com.app.bundlename (see How to run a fresh install of the application every time unit tests are run?)