iOS 7 - didUpdateLocations delegate not called after app goes in background - iphone

I'm able to receive location updates on all (simulators and iPad device) but my iPhone device.
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
self.locationManager.delegate = self;
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *newLocation = locations.lastObject;
NSLog(#"didUpdateLocations newLocation = %#", newLocation);
.
.
}
I receive didUpdateLocations (NSLog message) as long as app is in foreground but after I press Home button (i.e. app goes in background), I stop receiving notifications in didUpdateLocations. The 'console' doesn't show the NSLog message and thereafter even the location icon in status bar goes away within 10 seconds.
What works an what doesn't -
Appears that iOS 6 installed on my other iPhone 3gs works fine. The little location icon doesn't go away.
Test on iPad device with iOS 7 and the app keeps running in background.
All simulators (iPhone and iPad) with iOS 6 as well as iOS 7 works as expected.
When i test it on my iPhone 4 with iOS 7, the app doesn't run in background. So probably, this issue is only on iOS 7 (and even some specific iPhone devices).

I think I found the cause of this problem. The "Settings" app in iOS 7 has General > "Background App Refresh" which was turned OFF :(
As soon as I turned it "ON", my app started running in background :)

The above solution did not work for my particular case in iOS 8.X, therefore, I would like to post the solution that worked for me in hopes that it might help others.
I successfully setup silent Push Notifications which I was receiving in the Foreground, Background, and Suspended states. Once I received the PN I would need to obtain the location updates. I was able to do so while the application was in the Foreground, however, I was not able to do so while the application was in the background.
What I needed to do was add a key to the info.plist file called NSLocationAlwaysUsageDescription and give it a value. The value can be something like "Your location is needed in the background."
After adding the key I then added this code within my AppDelegate.m's application:didFinishLaunchingWithOptions: method -
// Check for iOS 8.X. Without this guard the code will crash with "unknown selector" on iOS 7.
if([[self locationManager] respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[[self locationManager] requestAlwaysAuthorization];
}
Once I added these two pieces I was able to successfully obtain location updates in the background.
Thanks,
audible

Related

App Rejected for failing to launch? ...and others

My game was just rejected from the AppStore for the following two reasons...
We found that your app failed to launch on iPhone 4 running iOS 5.1,
on both Wi-Fi and cellular networks.
We encountered the issue when selecting the application on the Home
screen - the app displayed a launch image then quit unexpectedly
And
Your app did not run at iPhone resolution and/or 2x iPhone 3GS resolution when reviewed on iPad.
Both of these confuse me, I currently do not have Lion installed so cannot test in 5.1 but, it works just fine in 5.0.1 and below.
Also, I have been testing on only my iPad, have no iphone to test on, but the game and graphics run just fine on the iPad. Though! I only have graphic files labeled #2x.png, and none with out it. Could that be the problem here?
If anyone has any advice that would be awesome, I responded to apple's rejection, but i bet it takes a while for them to get back to you, and i'd really like to move this along. Thanks!
**Updated
My didFinishLaunchingWithOptions:
Is as follows
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:NO];
mainMenu = [[MainMenu alloc] init];
self.navController = [[UINavigationController alloc] initWithRootViewController:mainMenu];
self.navController.navigationBar.hidden = YES;
[self.window addSubview:navController.view];
[self.window makeKeyAndVisible];
[[GCHelper sharedInstance] authenticateLocalUser];
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"missingWordsAdded"] intValue] != 1) {
[self forceAddWords];
}
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"EnableSoundEffects"]){
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"EnableSoundEffects"];
}
return YES;
}
GCHelper sets up GameCenter. thanks!
I've got a few suggestions:
Do a reset on your simulator, launch your program and see if it works
Uninstall your app from iPad, run it from Xcode and see if it works
Ask a friend to test an ad-hoc copy, or attach to your Xcode and run debug copy
Depending on your implementation, you should do some blind check on your startup code (didFinishLaunchingWithOptions or singleton instances). Especially those area that involves GCD, #synchronize, access on NSArray or NSDictionary. And things like default preference initializing and database creation are likely to produce this kind of problem.
Regarding the review process, my log indicates that Apple staff at Cupertino uses an iPad 2 with WiFi connection to run your app. They are not toying with you if they say they can't run your app.
1) "app failed to launch" for different configurations of phone and iOS is a big issue to reproduce because you may not have all the phones with different iOS versions at hand. You can try https://testflightapp.com/ for example, and can come to know your app performance with different configurations.
my app is also rejected for same reason. here u have first check the internet connectivity where it will needed than u have do your code.
to check internate connectivity u can use reachability classes.

monitoredRegions empty even though startMonitoringForRegion:desiredAccuracy: is called

I am developing a iPhone app running on iOS5, and am unable to set up geofences when I call the startMonitoringForRegion:desiredAccuracy: method on click of a button.
It works fine in the simulator when I print out the regions in monitoredRegions, but when running on an actual iPhone 4, the monitoredRegions is always empty. Expectedly, the didEnterRegion: and didExitRegion: methods are not called as well.
Another puzzling fact is that on BOTH the simulator and the iPhone 4 device, the CLLocationManagerDelegate method didStartMonitoringForRegion: is never called as well.
Would appreciate some help here, thank you!
EDIT:
this is method that I call on click of a button:
-(void) queueGeofence: (CLLocationCoordinate2D)selectedBranch userCoordinate:(CLLocationCoordinate2D)userCoordinate radius: (CLLocationDegrees)radius {
geofence = [[CLRegion alloc] initCircularRegionWithCenter:selectedBranch radius:radius identifier:#"geofence"];
CLLocationAccuracy acc = kCLLocationAccuracyNearestTenMeters;
[locationManager startMonitoringForRegion:geofence desiredAccuracy:acc];
[CLLocationManager regionMonitoringEnabled];
NSLog([CLLocationManager regionMonitoringEnabled] ? #"regionMonitoringEnabled:Yes" : #"regionMonitoringEnabled:No");
NSLog([CLLocationManager regionMonitoringAvailable] ? #"regionMonitoringAvailable:Yes" : #"regionMonitoringAvailable:No");
NSLog(#"LOCATIONMANAGER monitored regions: %#", [locationManager monitoredRegions]});
}
Region monitoring is both enabled and available, but monitoredRegions is still giving me back nothing.
If you look in CLLocationManager.h, the comments in the header for startMonitoringForRegion:desiredAccuracy: state that
If a region with the same identifier is already being monitored for this application, it
will be removed from monitoring. This is done asynchronously and may not be immediately reflected in monitoredRegions.
Therefore, you shouldn't necessarily expect that [locationManager monitoredRegions] would include your newly added region since it is added asynchronously.
Are you implementing the delegate method for locationManager:monitoringDidFailForRegion:withError:? Maybe that's getting called instead of locationManager:didStartMonitoringForRegion:. Also note that a region with the same identifier as an existing region will get removed, so you might be running into some unexpected problems because you're reusing "geofence" as your identifier.
First of all, you should be sure, that your app has a permission to use LocationManager. Check it when you alloc your manager.
[CLLocationManager authorizationStatus];
I had the same trouble when start app and decline a permission. And after deleting and rebuilding app. I had a flag, that user didn't accept it. Turn it on.
If you are just going by your NSLog, it probably isn't going to work. [locationManager monitoredRegions] returns an NSSet of CLRegions. They won't display to your log that way. Try this:
NSSet *setOfRegions = [locationManager monitoredRegions];
for (CLRegion *region in setOfRegions) {
NSLog (#"region info: %#", region);
}

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

iphone: location manager + adwhirl = battery drain :(

In my app I am using CLLocationManager and AdWhirl. I have made no specific development regarding background mode: I don't want my app to be working when it is in background, i.e. when the user press the "home button", GPS location should no be updated.
Yesterday evening I pressed "home button", and this morning the iPhone was out of battery. It's an iPhone 4 with iOS 4.1, not jailbreaked, and there is no background app running.
The battery was about 35% yesterday evening, and 0% this morning (iPhone was shutdown).
I have set breakpoint in my delegate, which is called each time GPS location is updated. When app is in background mode, delegate is not called. So I'm thinking GPS is really disabled in background mode: ok.
This morning, I am following battery drain: it's about 1% drop each 15 min. I think it a bit too much.
Should I do something specific when the app goes to background mode? Do you think this 1% drop is normal?
Yes, internet access and GPS are two big drains on battery. I don't know at all what you mean with normal, since no other apps are running you've concluded that that is in fact what happens :) Assuming you've tested with NO apps running and didn't get 1% per 15 minutes...
For adwhirl, it's unknown whether it already stops accessing the internet when the app goes into the background, but you can add this to your App Delegate:
- (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, called instead of applicationWillTerminate: when the user quits.
*/
[lm stopUpdatingLocation];
[adView ignoreAutoRefreshTimer]
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the active state: here you can undo many of the changes made on entering the background.
*/
[adView doNotIgnoreAutoRefreshTimer]
[lm startUpdatingLocation];
}
(lm and adView are the Location Manager object and the adWhirlView, both declared in the App Delegate. I've found it more useful to do all location managing via methods I make in the App Delegate.)

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?)