Requirement:
To Trigger Location update Callback after every second when the app is in background.
Problem:
Location Callbacks are not triggered after every second. Instead we get them inconsistently sometimes after 1 second, sometimes after 4 second and even with gap of 40-50 seconds.
Current Implementation:
setActivityType = CLActivityTypeOther
setAllowsBackgroundLocationUpdates = YES
setDesiredAccuracy = kCLLocationAccuracyBestForNavigation
setDistanceFilter = kCLDistanceFilterNone
setPausesLocationUpdatesAutomatically = false
plist configuration also done for background location updates.
Please suggest what more can be done to achieve solution for this problem?
Try out below code for background location updation when a significant location change is occured:
#pragma mark - CLLocationManager
- (void)startContinuosLocationUpdate
{
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusDenied)
{
NSLog(#"Location services are disabled in settings.");
}
else
{
// for iOS 8
if ([self.anotherLocationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[self.anotherLocationManager requestAlwaysAuthorization];
}
// for iOS 9
if ([self.anotherLocationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
{
[self.anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
[self.anotherLocationManager startUpdatingLocation];
}
}
- (void)stopContinuosLocationUpdate
{
[self.anotherLocationManager stopUpdatingLocation];
}
- (void)startMonitoringLocation
{
if (_anotherLocationManager)
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
self.anotherLocationManager = [[CLLocationManager alloc]init];
_anotherLocationManager.delegate = self;
_anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")) {
[_anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
else if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}
- (void)restartMonitoringLocation
{
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")) {
[_anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
else if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}
Related
I am looking for a solution to access/stop location services while app is in the background. My app takes continuous location when it's sent to background (It has access to continuous location) . It's necessary for the app functionality.
So I would like to know few things:
How long my app can take continuous location while it's still in the background? (before OS kills the background process or something like that)
If I want to add a timer say after 60 minutes app will stop taking the location, what would be the correct approach?
Background location updation can be done using following code:
In Appdelegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
// This "afterResume" flag is just to show that he receiving location updates
// are actually from the key "UIApplicationLaunchOptionsLocationKey"
self.shareModel.afterResume = YES;
[self.shareModel startMonitoringLocation];
}
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self.shareModel stopContinuosLocationUpdate];
[self.shareModel restartMonitoringLocation];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
//Remove the "afterResume" Flag after the app is active again.
self.shareModel.afterResume = NO;
[self.shareModel startContinuosLocationUpdate];
}
In Location update class, say LocationManager.m:
#import <CoreLocation/CoreLocation.h>
#property (nonatomic) CLLocationManager * anotherLocationManager;
- (void)startContinuosLocationUpdate
{
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusDenied)
{
NSLog(#"Location services are disabled in settings.");
}
else
{
// for iOS 8
if ([self.anotherLocationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[self.anotherLocationManager requestAlwaysAuthorization];
}
// for iOS 9
if ([self.anotherLocationManager respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)])
{
[self.anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
[self.anotherLocationManager startUpdatingLocation];
}
}
- (void)stopContinuosLocationUpdate
{
[self.anotherLocationManager stopUpdatingLocation];
}
- (void)startMonitoringLocation
{
if (_anotherLocationManager)
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
self.anotherLocationManager = [[CLLocationManager alloc]init];
_anotherLocationManager.delegate = self;
_anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_anotherLocationManager.activityType = CLActivityTypeOtherNavigation;
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")) {
[_anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
else if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}
- (void)restartMonitoringLocation
{
[_anotherLocationManager stopMonitoringSignificantLocationChanges];
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"9.0")) {
[_anotherLocationManager setAllowsBackgroundLocationUpdates:YES];
}
else if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
[_anotherLocationManager requestAlwaysAuthorization];
}
[_anotherLocationManager startMonitoringSignificantLocationChanges];
}
#pragma mark - CLLocationManager Delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
if(_dictLocation && [_dictLocation isKindOfClass:[NSDictionary class]])
{
float latitudeValue = [[RVCommon validateDataForNumber:_dictLocation[#"lat"]] floatValue];
float longitudeValue = [[RVCommon validateDataForNumber:_dictLocation[#"lng"]] floatValue];
CLLocation *facilityLocation = [[CLLocation alloc] initWithLatitude:latitudeValue longitude:longitudeValue];
CLLocation *mostRecentLocation = locations.lastObject;
CLLocationDistance distanceInMeters = [mostRecentLocation distanceFromLocation:facilityLocation];
if (distanceInMeters <= 500.0)
{
//Here I am informing the server when user is within 500mts of the coordinate.
}
}
NSLog(#"locationManager didUpdateLocations: %#",locations);
}
How to get continuous location details when app is killed/Terminated ? startMonitoringSignificantLocationChanges method firing the didUpdateLocations delegate method after 500 meters. But i want location updates in kill mode for every 10 meters. Or i want to relaunch the application in background automatically and start the location updates in background.Currently i have the working code in background.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
[[LocationManager sharedLocarionManager] updateAccuracy:YES];
[[LocationManager sharedLocarionManager].locationManager requestAlwaysAuthorization];
[[LocationManager sharedLocarionManager].locationManager stopUpdatingLocation];
[[LocationManager sharedLocarionManager].locationManager startUpdatingLocation];
[[LocationManager sharedLocarionManager].locationManager stopMonitoringSignificantLocationChanges];
[[LocationManager sharedLocarionManager].locationManager startMonitoringSignificantLocationChanges];
}
return YES;
}
LocationManager.m
- (id)init {
self = [super init];
if(self != nil) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 100; // meters
self.locationManager.delegate = self;
}
return self;
}
-(void)updateAccuracy:(BOOL)trackingAccuracy {
if (trackingAccuracy) {
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 10.0 ; // meters
self.locationManager.allowsBackgroundLocationUpdates = YES;
} else {
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
self.locationManager.distanceFilter = 100; // meters
}
[ self.locationManager startUpdatingLocation];
[self.locationManager startMonitoringSignificantLocationChanges];
}
There is a work around. Though you cannot get continuous location when your app is killed but you can send silent push notification via server to your user to get the update about its location. You will get 30 second window when the app receives silent push notification. In that short time span, you have to perform your task.
Please don't mark it as duplicate because I took help from others posted answer in stack overflow. But still I faced some issues. Location manager delegate not get called even in real device also. And also it's not asking for the permission.
Below is my code.
- (IBAction)getUserLocationAction:(id)sender
{
if([CLLocationManager locationServicesEnabled])
{
if(!locationManager)
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
if ([locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[locationManager requestWhenInUseAuthorization];
}
[locationManager startUpdatingLocation];
//[locationManager startMonitoringSignificantLocationChanges];
}
else
{
NSLog(#"Location service is not enabled");
}
}
#pragma mark - Location Manager Delegate-
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"%#", locations);
userLocation = [locations lastObject];
NSLog(#"User Current Locatiion\nLat: %+.6f\nLong: %+.6f", [userLocation coordinate].latitude, [userLocation coordinate].longitude);
[locationManager stopUpdatingLocation];
//[locationManager stopMonitoringSignificantLocationChanges];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(#"Error: %#", [error localizedDescription]);
}
I also add the below two key in my .plist file
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app want to use your location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app want to use your location</string>
I have also added the frameworks
I am not sure where I am doing wrong. Please help me.
UPDATE
Firstly I think its an issue with my code but after some research it's working as expected in iOS7. The code is remain same, this issue occur only in iOS8. Please let me know what changes needs to be done for working in iOS8 also.
You need to do some more checking:
- (IBAction)getUserLocationAction:(id)sender
{
if([CLLocationManager locationServicesEnabled]) {
if(!self.locationManager) {
self.locationManager = [[CLLocationManager alloc] init];
[self.locationManager setDelegate:self];
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
}
CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
if (authStatus == kCLAuthorizationStatusNotDetermined) {
// Check for iOS 8 method
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
else {
[self.locationManager startUpdatingLocation];
}
}
else if(authStatus == kCLAuthorizationStatusAuthorizedAlways || authStatus == kCLAuthorizationStatusAuthorizedWhenInUse || authStatus == kCLAuthorizationStatusAuthorized) {
[self.locationManager startUpdatingLocation];
}
else if(authStatus == kCLAuthorizationStatusDenied){
NSLog(#"User did not allow location tracking.");
// present some dialog that you want the location.
}
else {
// kCLAuthorizationStatusRestricted
// restriction on the device do not allow location tracking.
}
}
else {
NSLog(#"Location service is not enabled");
}
}
Also you need to handle the use callback:
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status != kCLAuthorizationStatusRestricted && status !=kCLAuthorizationStatusDenied) {
[self.locationManager startUpdatingLocation];
}
}
Check your Location Access settings..so go to Setting-->Privacy-->Location Services-->Your app
I'm using Location service in my app.
But [CLLocationManager authorizationStatus] is kCLAuthorizationStatusNotDetermined both before [self.locationManager startUpdatingLocation];
code of project:
Implementation
#import <CoreLocation/CoreLocation.h>
#interface NewRunViewController () <CLLocationManagerDelegate>
#property (nonatomic, strong) CLLocationManager *locationManager;
- (void)viewDidLoad
{
[super viewDidLoad];
[self startLocationUpdates];
}
- (void)startLocationUpdates
{
// Create the location manager if this object does not
// already have one.
if (self.locationManager == nil) {
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = 10;
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];
}
I also add NSLocationWhenInUseUsageDescription key into info.plist file.
And app also does not ask for user permission to use core location service.
Can anyone help me??
You must include a string value for the key NSLocationAlwaysUsageDescription for your target.
Look on the Info page of your target in Xcode. The string will be the text for the alert when the system asks the user for permission to use location services in your app.
This is my own code for handling same. It is requesting Always Authorization but you could replace with When in Use.
CLAuthorizationStatus auth = [CLLocationManager authorizationStatus];
if (auth == kCLAuthorizationStatusNotDetermined && [self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager performSelector:#selector(requestAlwaysAuthorization) withObject:NULL];
} else {
if (auth == kCLAuthorizationStatusAuthorizedAlways) {
[self notificationsSetup];
} else {
NSLog(#"Device is not authorized for proper use of the location Services, no logging will be performed");
}
}
You might find my github repository helpful- TTLocationHandler on Githup
you have to move
[self.locationManager startUpdatingLocation];
to the delegate method
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
case kCLAuthorizationStatusNotDetermined:
{
NSLog(#"User still thinking");
}
break;
case kCLAuthorizationStatusDenied:
{
NSLog(#"User denied location request");
}
break;
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways:
{
[self.locationManager startUpdatingLocation];
}
break;
default:
break;
}
}
Update : Make the log easier to be understood
Thank you guys for your answers. I found solution of problem. I checked the version of system like:
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
in startLocationUpdates method:
if(IS_OS_8_OR_LATER) {
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
}
Now it's working properly.
I have a question about multithreading in iOS6.
I make app, that starts and stops CLLocationManager, when user press the UISwitch.
I start CLLocationManager in separate thread (I don't want user seen any delay in main thread) , but sometimes UISwitch didn't change it's value. Delay occurs. I think it is because of starting CLLocationManager. Why it happens? I thought, that if I use threading, there will be no delay in main thread.
The code:
- (IBAction)useGPS:(UISwitch *)sender {
if (![trackLocationSwitch isOn]) {
[useGPSSwitch setOn:NO];
return;
}
if ([sender isOn]) {
[accuracyLabel setText:#"Satellite"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[trackLocation startTrackLocationUsingOnlyGPS:YES];
});
} else {
[accuracyLabel setText:#"Cellular"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[trackLocation stopTrackLocation];
[trackLocation startTrackLocationUsingOnlyGPS:NO];
});
}
TrackLocation.m:
- (void)startTrackLocationUsingOnlyGPS:(BOOL)useOnlyGPS {
if ( useOnlyGPS ) {
[self restartLocationManagerWithBestAccuracy:YES];
useBestAccuracy = YES;
onlyGPS = YES;
} else {
[self restartLocationManagerWithBestAccuracy:NO];
useBestAccuracy = NO;
onlyGPS = NO;
}
- (void)stopTrackLocation {
[locationManager stopUpdatingLocation];
[locationManager stopMonitoringSignificantLocationChanges];
}
- (void)restartLocationManagerWithBestAccuracy:(BOOL)_useBestAccuracy {
if ( _useBestAccuracy ) {
useBestAccuracy = YES;
[locationManager stopUpdatingLocation];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager setPausesLocationUpdatesAutomatically:NO];
[locationManager setActivityType:CLActivityTypeFitness];
[locationManager setDistanceFilter:100];
[locationManager startUpdatingLocation];
} else {
useBestAccuracy = NO;
[locationManager stopUpdatingLocation];
[locationManager startMonitoringSignificantLocationChanges];
}
}