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
Related
I made an application that monitors updated location in background after terminated like Moves.
In order to implement this app, I made a location manager class including <CoreLocation/CoreLocation.h> and CLLocationManagerDelegate and enabled 'Location updates' and 'Background fetch' modes in Capabilities. And in order for checking out, I added a node.js module which is connected to remote server, and this application send a message to remote server when new location is updated in background (after terminated, not remained in suspended apps queue).
And the location manager is declared and called in viewDidLoad of ViewController.
The question is why is the ViewController called when the app runs in background mode after terminated like above flow?
I just thought that the location manager would be called only because it contains CLLocationManagerDelegate, but I was wrong. The background task started from viewDidLoad method.
Dose anybody know why is this happened? I really want to know which functions are called from the beginning in background mode after terminated.
EDIT:
Here are my codes for updating current location in background after terminated. I also added capabilities which are 'Location updates' and 'Background fetch'.
ViewController.m (viewDidLoad method)
[super viewDidLoad];
locationManager = [[LocationManager alloc] initWithDelegate:self];
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
switch(state){
case UIApplicationStateActive:
case UIApplicationStateInactive:
[locationManager startLocationService:NO];
break;
case UIApplicationStateBackground:
[locationManager startLocationService:YES];
break;
}
LocationManager.m (init method)
isCalledAfterTerminated = flag;
if(!socketIO.isConnected){
[socketIO connect];
}
locationManager = [[CLLocationManager alloc] init];
if ([locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]){
[locationManager requestAlwaysAuthorization];
}
// for iOS 9
if ([locationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)]){
[locationManager setAllowsBackgroundLocationUpdates:YES];
}
locationManager.delegate = self;
locationManager.pausesLocationUpdatesAutomatically = NO;
if(isCalledAfterTerminated){
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
locationManager.distanceFilter = DISTANCE_FILTER_TERMINATED;
[locationManager startMonitoringSignificantLocationChanges];
} else {
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = DISTANCE_FILTER_RUNNING;
[locationManager startUpdatingLocation];
}
I'm trying to implement the deferred location updates to have a better battery consumption.
I'm starting my location manager like this :
- (void)initCoreLocation
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.pausesLocationUpdatesAutomatically = YES;
self.locationManager.activityType = CLActivityTypeAutomotiveNavigation;
//Très important pour iOS9 !
if ([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]) {
self.locationManager.allowsBackgroundLocationUpdates=YES;
}
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
And starting deferred update like this way:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
if (!self.deferringUpdates) {
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:30];
self.deferringUpdates = YES;
}
}
-(void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error { // Stop deferring updates
if(error) {
NSLog(#"error");
}
NSLog(#"didFinishDeferredUpdates");
self.deferringUpdates = NO;
}
I have didFinishDeferredUpdates log every 30 seconds, however didUpdateLocations keeps calling every second, removing any try to optimise the battery consumption. Is it supposing to the location manager to call the didUpdateLocations every 30 seconds ?
Maybe you are not having the right approach as allowDeferredLocationUpdatesUntilTraveled() tells the GPS hardware to store new locations internally until the specified distance or timeout conditions are met.
From the iOS Developer Library:
If your app is in the foreground, the location manager does not defer the deliver of events but does monitor for the specified criteria. If your app moves to the background before the criteria are met, the location manager may begin deferring the delivery of events. Link
Are you debugging?
As stated in the answer here: ios deferred location updates fail to defer
Deferred updates are delivered only when the system enters a low power state. Deferred updates do not occur during debugging because Xcode prevents your app from sleeping and thus prevents the system from entering that low power state.
I believe you only need one of them at one time, change the call in applicationDidEnterBackground and applicationWillEnterForeground
- (**void)applicationDidEnterBackground:(UIApplication *)application {
// Need to stop regular updates first
[self.locationManager stopUpdatingLocation];
// Only monitor significant changes
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
[self.locationManager stopMonitoringSignificantLocationChanges];
[self.locationManager startUpdatingLocation];
}
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];
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.