On early 2014, Apple has updated the iOS 7.0 to 7.1 in order to allow location updates even when the app is not active on foreground and not in the background. How do we do that?
I have read some articles like Apple's iOS 7.1 will fix a geolocation bug. But Apple didn't provide much communication related to that nor any sample code on how to get the location update even when the app is killed/terminated/suspended.
I have read the Release Notes for iOS 7.1. I couldn't find anything related to that as well.
So, how do we actually get the location update for iOS 7 and 8 even when the app is suspended?
After months of trials and errors by experimenting the Core Location Framework, I have found the solution to get location update even when the app is killed/suspended. It works well for both iOS 7 and 8.
Here is the solution:-
If your app is a location based mobile application that needs to monitor the location of the device when it has significant changes, the iOS will return you some location coordinates when the device has moved more than 500 meters from the last known location. Yes, even when the app is killed/suspended either by the user or iOS itself, you still can get the location updates.
So in order for a locationManager to get location update even when the app is killed/suspended, you must use the method startMonitoringSignificantLocationChanges, you can not use startUpdatingLocation.
When iOS wants to return the location update to the app, it will help you to relaunch the app and return a key UIApplicationLaunchOptionsLocationKey to the app delegate method didFinishLaunchingWithOptions.
The key UIApplicationLaunchOptionsLocationKey is very important and you must know how to handle it. You must create a new locationManager instance when you receive the key and you will get the location update on the locationManager delegate method didUpdateLocations.
Here is the sample code:-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
self.shareModel = [LocationShareModel sharedModel];
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
self.shareModel.anotherLocationManager = [[CLLocationManager alloc]init];
self.shareModel.anotherLocationManager.delegate = self;
self.shareModel.anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.shareModel.anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(IS_OS_8_OR_LATER) {
[self.shareModel.anotherLocationManager requestAlwaysAuthorization];
}
[self.shareModel.anotherLocationManager startMonitoringSignificantLocationChanges];
}
return YES;
}
In addition to the didFinishLaunchingWithOptions method, I have created the locationManager instance when the app is active. Here are some code examples:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.shareModel.anotherLocationManager stopMonitoringSignificantLocationChanges];
if(IS_OS_8_OR_LATER) {
[self.shareModel.anotherLocationManager requestAlwaysAuthorization];
}
[self.shareModel.anotherLocationManager startMonitoringSignificantLocationChanges];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(self.shareModel.anotherLocationManager)
[self.shareModel.anotherLocationManager stopMonitoringSignificantLocationChanges];
self.shareModel.anotherLocationManager = [[CLLocationManager alloc]init];
self.shareModel.anotherLocationManager.delegate = self;
self.shareModel.anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.shareModel.anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(IS_OS_8_OR_LATER) {
[self.shareModel.anotherLocationManager requestAlwaysAuthorization];
}
[self.shareModel.anotherLocationManager startMonitoringSignificantLocationChanges];
}
I have written an article explaining on the details on how to get the location update for iOS 7 and 8 even when the app is killed/suspended. I have also uploaded the complete source code on GitHub with the steps on how to test this solution.
Please visit the following URLs for more information:-
Getting Location Updates for iOS 7 and 8 when the App is Killed/Terminated/Suspended
Source Code on GitHub - Get the Location Updates Even when the iOS mobile apps is Suspended/Terminated/Killed
locationManager = [[CLLocationManager alloc] init];
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
if(IS_OS_8_OR_LATER)
{
[locationManager requestWhenInUseAuthorization];
}
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; //whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
[locationManager startUpdatingLocation];
that code user location update only forground app running but not background run
[locationManager requestWhenInUseAuthorization];
Related
We implemented a functionality that tracks the user location on the background at least every 15 minutes. At the moment the background tracking works great for a couple of hours, some cases up until 8 hours, and other cases can go up to 6 days.
The issue is that at a certain point while the user is stationary the app stops managing the location updates for no apparent reason, and reopening the application will not resume the tracking process. We know that the app is not managing the location updates because the device logs show an event from locationd sending the position to our app, but there's no code execution.
We understand that the OS at any time can terminate the application, but we would like to be able to manage those cases to correctly communicate this behavior to the user that expects tracking to keep functioning or for the tracking to continue sending locations in the background.
This issue seems to be inconsistent across our user base, we've identified that users with iOS 14.4+ and from the iPhone 8 up to the iPhone 13 present this problem.
Our current configuration for the location manager is:
AppDelegate.m
#implementation AppDelegate
Location *geoData;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
///...
if (geoData == nil) {
geoData = [Location sharedInstance];
}
///...
}
- (void)applicationWillTerminate:(UIApplication *)application{
geoData = nil;
}
Location.h
#property (nonatomic, retain) CLLocationManager *locationManager;
Location.m
_locationManager = [[CLLocationManager alloc] init];
[_locationManager setDelegate:self];
if ([_locationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[_locationManager requestAlwaysAuthorization];
}
if ([_locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]) {
_locationManager.allowsBackgroundLocationUpdates = YES;
}
_locationManager.pausesLocationUpdatesAutomatically = NO
_locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
_locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[_locationManager startUpdatingLocation];
We receive the updates through the didUpdateLocations where we send the data to an external API server.
The app is configured with Background fetch and Location updates background modes.
My app requires frequent location updates when in use and when running in the background.
I have enbabled Background Modes --> Location updates for my app but my app stops receiving location updates about 10 seconds after entering background mode.
My code:
func initializeLocationManager()
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.distanceFilter = 1
}
func startLocationManager()
{
locationManager.startUpdatingLocation()
}
I've also added the following to the info.plist:
NSLocationAlwaysUsageDescription
Any help would be appreciated.
Thanks.
This fixed my issue:
if #available(iOS 9.0, *) {
locationManager.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
}
To get consistent updates, you might have to do any action every 5 minutes. How to do it is described here and here.
Here is the solution from Ricky here:-
If your app is a location based mobile application that needs to monitor the location of the device when it has significant changes, the iOS will return you some location coordinates when the device has moved more than 500 meters from the last known location. Yes, even when the app is killed/suspended either by the user or iOS itself, you still can get the location updates.
So in order for a locationManager to get location update even when the app is killed/suspended, you must use the method startMonitoringSignificantLocationChanges, you can not use startUpdatingLocation.
When iOS wants to return the location update to the app, it will help you to relaunch the app and return a key UIApplicationLaunchOptionsLocationKey to the app delegate method didFinishLaunchingWithOptions.
The key UIApplicationLaunchOptionsLocationKey is very important and you must know how to handle it. You must create a new locationManager instance when you receive the key and you will get the location update on the locationManager delegate method didUpdateLocations.
Here is the sample code:-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
self.shareModel = [LocationShareModel sharedModel];
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
self.shareModel.anotherLocationManager = [[CLLocationManager alloc]init];
self.shareModel.anotherLocationManager.delegate = self;
self.shareModel.anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.shareModel.anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(IS_OS_8_OR_LATER) {
[self.shareModel.anotherLocationManager requestAlwaysAuthorization];
}
[self.shareModel.anotherLocationManager startMonitoringSignificantLocationChanges];
}
return YES;
}
In addition to the didFinishLaunchingWithOptions method, I have created the locationManager instance when the app is active. Here are some code examples:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[self.shareModel.anotherLocationManager stopMonitoringSignificantLocationChanges];
if(IS_OS_8_OR_LATER) {
[self.shareModel.anotherLocationManager requestAlwaysAuthorization];
}
[self.shareModel.anotherLocationManager startMonitoringSignificantLocationChanges];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(self.shareModel.anotherLocationManager)
[self.shareModel.anotherLocationManager stopMonitoringSignificantLocationChanges];
self.shareModel.anotherLocationManager = [[CLLocationManager alloc]init];
self.shareModel.anotherLocationManager.delegate = self;
self.shareModel.anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.shareModel.anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(IS_OS_8_OR_LATER) {
[self.shareModel.anotherLocationManager requestAlwaysAuthorization];
}
[self.shareModel.anotherLocationManager startMonitoringSignificantLocationChanges];
}
I have written an article explaining on the details on how to get the location update for iOS 7 and 8 even when the app is killed/suspended. I have also uploaded the complete source code on GitHub with the steps on how to test this solution.
Please visit the following URLs for more information:-
1. Getting Location Updates for iOS 7 and 8 when the App is Killed/Terminated/Suspended
2. Source Code on GitHub - Get the Location Updates Even when the iOS mobile apps is Suspended/Terminated/Killed
I have an app that uses background location updates, it needs to constantly track the devices position regardless if the app is in the foreground or background. I have the background tracking setup in the app delegate.
In the front end I have a single UIViewController with a working mapkit view all the CCLocationManager code triggers without error but didUpdateUserLocation is never fired within the custom UIViewController class.
Background location tracking is working with absolutely no problems.
Here is the code i'm using in viewDidLoad
[self.mapView setShowsUserLocation:YES];
[self.mapView setShowsPointsOfInterest:YES];
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager requestWhenInUseAuthorization];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
[self.locationManager setDelegate:self];
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
NSString *error;
if (![CLLocationManager locationServicesEnabled]) {error = #"Error message";}
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusRestricted || status == kCLAuthorizationStatusDenied) {error = #"Error message";}
if (error) {NSLog(error);}
else
{
status = [CLLocationManager authorizationStatus];
self.locationManager.pausesLocationUpdatesAutomatically = NO;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
}
Any help would be appreciated
Thanks
You need to check below details:
Testing this on simulator?? didUpdateLocations must be tested on real device.
Make sure that you specified the NSLocationAlwaysUsageDescription string or else the app won't present the authorization message, and you won't be authorized.
Have you implemented locationManager:didFailWithError:? did you got any error?
Have you implemented locationManager:didChangeAuthorizationStatus:? Are you getting this called with a successful authorization status?
I worked out the problem finally.
Turns out even if you're using the location stuff in Schema and Xcode you still need to set it in the Simulator via Debug > Location
Hopefully this helps someone else.
and now, with iOS 9, the following needs to be included for any update;
if ([ locationManager respondsToSelector:#selector(requestWhenInUseAuthorization )])
{
locationManager.allowsBackgroundLocationUpdates = YES;
}
Else background updates will be impaired.
I am developing the IOS app with location server. In the app I am checking the user location periodically, and send the location details to the server.
Following are the steps:
1: App registers for location updates( in plist).
2: added the code like:
-(void)startUpdating{
locationManager = [[CLLocationManager alloc]init];
locationManager.delegate = self;
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
locationManager.pausesLocationUpdatesAutomatically = YES;
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
locationManager.distanceFilter = kCLDistanceFilterNone;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
dispatch_async(dispatch_get_main_queue(), ^{
//call web service location
[self UpdateAppmyLocation:#"1"];
});
[locationManager allowDeferredLocationUpdatesUntilTraveled:200 timeout:(NSTimeInterval)90];
}
- (void)applicationWillResignActive:(UIApplication *)application
{
[locationManager startUpdatingLocation];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[locationManager stopUpdatingLocation];
}
3: startUpdating method called in didFinishLaunchingWithOptions.
Everything is working, but the didUpdateLocations:locations method called every minute. I am trying to add delay for some time so it will not lead to battery issue.
Note: Testing the app on iPhone 5 with iOS 7.1
Please let me know the solution.
https://github.com/voyage11/Location
Use this code.you can set time after how much time you want to call it.
I have tried many codes, but i found this as most accurate and list battery issue.This is also awesome for background location service.
Since I updated to iOS SDK 8 and 8.1, there have been many problems, including that the method of CLLocationManager didUpdateLocations is no longer called.
Here the code:
In viewDidLoad:
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
The method:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{ NSLog(#"Call?"); }
A couple of thoughts:
Are you testing this on simulator or on device? You must test this on actual device. FWIW, didUpdateLocations works fine on my iOS 8.1 device.
Make sure that you specified the NSLocationAlwaysUsageDescription string or else the app won't present the authorization message, and you won't be authorized.
Obviously, if you don't need "always usage", you should just requestWhenInUseAuthorization and then you must specify NSLocationWhenInUseUsageDescription string.
Have you checked [CLLocationManager authorizationStatus]? I do that before I even try to authorize or start location services, because if it's either kCLAuthorizationStatusDenied or kCLAuthorizationStatusRestricted, the location services won't work until the user goes to settings and remedies that. You may want to present them a message to that effect if you get either of these authorization codes.
Have you implemented locationManager:didFailWithError:? Does it report anything?
Have you implemented locationManager:didChangeAuthorizationStatus:? Are you getting this called with a successful authorization status?
Need include MapKit.framework from Capabilities:
I had the same problem, and my code (viewdidupdate method) was missing this:
_mapView.delegate = self;
For some reason the app worked in ios 7 without the line, but in ios 8.1 didUpdateUserLocation was never called.
Specify both NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription in your plist:
Also in code, request authorization:
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) { // or requestAlwaysAuthorization
[self.locationManager requestWhenInUseAuthorization]; // or requestAlwaysAuthorization
}
[self.locationManager startUpdatingLocation];