In my app, I have the following code;
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Start location services
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
locationManager = [[CLLocationManager alloc] init];
etc...
Somehow, when I do a fresh install, as soon as the location manager is started, the app gets in a loop, repeating the "Do you allow this app to use Location Services"-dialog faster than I can click OK or Cancel.
The way to get out of that loop is to switch to the Settings and manually
approve the usage.
This is iOS8, and I DID add the mandatory strings in the .plist.
What should I do?
Your problem is that you are requesting permission in applicationDidBecomeActive - When the permission dialog is shown your application becomes inactive (because there is a system dialog that is active) and then once the dialog is dismissed it becomes active again - but the permission has not yet been processed, so the dialog is shown and so on.
You should request location permission in another method - either applicationDidFinishLaunchingWithOptions: or in your view controller or other class where you want to instantiate your CLLocationManager - didBecomeActive is not a good place to do this.
You could init the CLLocationManager before requesting authorisation, but I would also recommend against just assigning for permission right away. The link here has a good write up on the most effective way to ask for permission: http://techcrunch.com/2014/04/04/the-right-way-to-ask-users-for-ios-permissions/
Related
I'm Using CLLocationManager to monitor user location in my app. Steps to produce the issue are listed down:-
When i invoke CLLocationmanager's requestwheninuseauthorization method for the first time, it presents the location access permission alert & i don't make a choice on allow or don't allow buttons.
Now if phone is locked due to being idle for some time or interrupted by a phone call.
I unlocks the phone & see the my app which was already running in foreground with location access permission dialogue, now after unlocking phone i don't find the location access permission alert on the screen within my app. It just disappears, despite the fact that neither i rejected nor accepted to provide location access.
What could be the reason for that & how can i resolve this issue, else what could be a workaround as it seems an issue with the iOS. As per my requirement user must provide location access before using my app.
As hinted by Sandeep , I have added observer of the UIApplicationDidBecomeActiveNotification by using below code in viewDidLoad: method of my viewcontroller.
[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didBecomeActive)
name:UIApplicationDidBecomeActiveNotification
object:nil];
Now i have again presented the location authorisation dialogue in my didBecomeActive method of viewcontroller.
- (void)didBecomeActive {
// Here put your conditions to check that user hasn't already authorised or rejected the location access permissions.
[self.locationManager requestWhenInUseAuthorization];
}
So now whenever user unlocks the device the authorization dialogue is always there due to above mentioned implementation.
Whenever you want to access the location info you can use this to test if user has granted the location access permission or not :)
BOOL isAuthorized = [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse;
BOOL isTurnedOn = [CLLocationManager locationServicesEnabled];
if (isAuthorized || !isTurnedOn) {
[self.locationManager startUpdatingLocation];
}
else {
[self.locationManager requestWhenInUseAuthorization];
}
I am trying to make an app to track the user GPS all the time, this app is a kind of car GPS tracker to get the location of driver all the time and send it to server.
I have tried to add "location updates" to the "background modes" but the app will automatically suspends after 10 mins when going into background.
Is there a way to make this app run all the time and get the GPS location?
You have two options here:
1) Regular location tracking. This type of tracking works with kCLAuthorizationStatusAuthorizedWhenInUse and kCLAuthorizationStatusAuthorizedAlways authorizations. When CLLocationManager started tracking location once it will receive location updates in delegate method locationManager:didUpdateLocations:. App can go to suspended state, but when location manager receive new location app goes to background state and handles new location in delegate method. How to setup location manager:
- (void)viewDidLoad {
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
// Setup location tracker accuracy
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
// Distance filter
self.locationManager.distanceFilter = 50.f;
// Assign location tracker delegate
self.locationManager.delegate = self;
// This setup pauses location manager if location wasn't changed
[self.locationManager setPausesLocationUpdatesAutomatically:YES];
// For iOS9 we have to call this method if we want to receive location updates in background mode
if([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]){
[self.locationManager setAllowsBackgroundLocationUpdates:YES];
}
[self.locationManager startUpdatingLocation];
}
2) Signification location changes tracking. This type of tracking works only with kCLAuthorizationStatusAuthorizedAlways authorization. It receives new location only each 500 meters, so distance filter and desiredAccuracy don't work here. App can go to suspended state, and even can be terminated by system, but when location updates app goes to background state and receives location in delegate method locationManager:didUpdateLocations:.If app was terminated by system, it will be relaunched in background with UIApplicationLaunchOptionsLocationKey key in launch options in didFinishLaunchingWithOptions app delegate method. How to setup this type on tracking:
- (void)viewDidLoad {
[super viewDidLoad];
self.locationManager = [[CLLocationManager alloc] init];
// Assign location tracker delegate
self.locationManager.delegate = self;
// For iOS9 we have to call this method if we want to receive location updates in background mode
if([self.locationManager respondsToSelector:#selector(allowsBackgroundLocationUpdates)]){
[self.locationManager setAllowsBackgroundLocationUpdates:YES];
}
[self.locationManager startMonitoringSignificantLocationChanges];
}
You should notice that both of these methods does not guarantee that your application does not go to suspended state.
Also, if app was terminated by user (for example from app switcher by swipe) location tracking in background will not work.
UPDATE (corresponding to comments)
Here is my code examples that work for me:
For Regular tracking. Run the example, provide access to user location, tap Start button to start location updates. To test locations in simulator choose in simulator menu Debug > Location > Freeway Drive. Now you can push app to background by home button (Command+Shift+H). Leave simulator for more than 10 minutes, and all this time app will receive locations. When you return to app you will see red pins on the map.
For Significant changes. Run the app and test by the same way as for previous example.
Monitoring Significant changes can be started only by method [self.locationManager startMonitoringSignificantLocationChanges];
UPDATE (iOS 11)
Changes to location tracking in iOS 11
iOS 11 also makes some major changes to existing APIs. One of the affected areas is location tracking. If your app only uses location while the app is in the foreground, as most apps do, you might not have to change anything at all; however, if it’s one of those apps that continuously track user’s location throughout the day, you should probably book some time this summer for making some changes in how you do the tracking and testing possible usage scenarios.
follow this link: https://mackuba.eu/2017/07/13/changes-to-location-tracking-in-ios-11/
I am sure it is useful for the author because the question was asked in Feb 2016 and I am giving an answer in June 2019. This answer maybe is useful for other users.
Recently, I was working with the same requirement. After 2-3 week hard work, I did it. For other users, I create a helper class for it. Which is available in GitHub.
Please use HSLocationManager for your requirement. I have achieved the same requirements in one of my project
Location manager that allows getting background location updates every
n seconds with desired location accuracy.
Advantage:
OS will never kill our app if the location manager is currently
running.
Give periodically location update when it required(range is between 2 -
170 seconds (limited by max allowed background task time))
Customizable location accuracy and time period.
Low memory consumption(Singleton class)
In reply to comment 1 in the solution (I can't comment anywhere yet): you didn't seem to solve the problem as your app gets suspended and doesn't update the location any more after 10 minutes.
I had the same issue: I had set setAllowsBackgroundLocationUpdates to YES, and I had the NSLocationAlwaysUsageDescription key in my Info.plist, but my App also used to stop tracking location after 10 minutes.
I solved it by adding both NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription to the Info.plist file so it looks like this:
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs to use your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs to use your location</string>
set it.it take power of battery but your application run in Background.OS does not Suspend your App
[self.locationManager setPausesLocationUpdatesAutomatically:NO];
Currently in this iOS application I have added some controller I've written as an observer to the [NSNotificationCenter defaultCenter] to observe NSUserDefaultsDidChangeNotification.
- (void)startObservingDefaults
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(userDefaultsDidChange)
name:NSUserDefaultsDidChangeNotification
object:nil];
}
The user default that I am watching should enable or disable a CLLocationManager using a PSToggleSwitchSpecifier from the settings menu. After starting the application and sending startUpdatingLocation to a CLLocationManager ivar I can go to the settings menu and disable the location manager. My selector userDefaultsDidChange will then check if that default is enabled or disabled. If it is enabled, I send startUpdatingLocation to the location manager instance, if it is disabled I send stopUpdatingLocation to it. Each path logs its action like so
- (void)userDefaultsDidChange
{
if ([self duringOperationHours] && [self isEnabled]) {
NSLog(#"\n\n\nWithin Operating Hours.\n\n\n");
[_locationManager startUpdatingLocation];
NSLog(#"Enabled Location Manager.");
} else {
NSLog(#"\n\n\nDisabled or Outside of Operating Hours.\n\n\n");
[_locationManager stopUpdatingLocation];
NSLog(#"Disabled Location Manager.");
}
}
I have other settings that control time of day (start time and end time ex. 6AM to 6PM). If I change one of those settings and the current time is still within that time range the log will show that it is still "Within Operating Hours." and that the location manager is enabled.
If I change it so that the location manager gets disabled, it does log both NSLog statements in my method above and it DOES shut down the location manager.
The problem comes when I try to start it back up. So say I am in the settings and it is enabled. I disable it, the log shows it is disabled and the little icon for GPS disappears, if I then re-enable it the notification doesn't fire at all. In fact, no other settings even in other child panes don't fire until I go back to the application. (I've tried a dispatch_async inside the userDefaultsDidChange method to both main_queue and global_queue and saw no change).
MY QUESTION IS am I doing something wrong that might cause this to lock up with the location manager? Or is this some internal queue issue to CLLocationManager? Or something else? Perhaps CLLocationManager will only start if the application is in the foreground? Even if that were the case I should still be seeing the log statements that it at least attempted to start the location manager right?
Nevermind I figured it out.
The observation of the NSNotificationCenter on the NSUserDefaultsDidChangeNotification DOES NOT wake up the application. What was happening was the CLLocationManagerDelegate protocol applied to my controller would "wake up" the application to do what it needed to the with CLLocations that were received. Upon this waking up, the observation of change to some user defaults in the NSNotificationCenter would fire off my selector userDefaultsDidChange.
As long as the CLLocationManager was updating and there to alert the application it would allow the controller to call the designated selector. When the CLLocationManager was no longer updating the location the application would not be woken up and therefore not know any user defaults had changed until the app returned to the foreground.
I will update this answer if I come up with a better solution for this odd observation scenario.
Suppose in the starting the location services are off in the default settings page. I have a button in the app to turn on the location services if first time I click on that it shows the default alert to change the settings to turn on
locationmanager = [[CLLocationManager alloc]init];
[locationmanager setDelegate:self];
[locationmanager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationmanager startUpdatingLocation];
It is working fine two times. but if it got third time location services are in off condition and click on on button it doesn't show any alert. I am unable to know the CLLocation behavior. May b its not a good question to ask but still I want to clear this concept. if anyone has some idea then please help me out.
Thank You.
Here's what Apple documentation says:
In addition to hardware not being available, the user has the option of denying an application’s access to location service data. During its initial uses by an application, the Core Location framework prompts the user to confirm that using the location service is acceptable. If the user denies the request, the CLLocationManager object reports an appropriate error to its delegate during future requests. You can also check the application’s explicit authorization status using the authorizationStatus method.
So the alert could or could not appear, based on authorizationStatus.
I have something like this:
CLLocationManager *locManager = [[CLLocationManager alloc] init];
locManager.delegate = self;
locManager.desiredAccuracy = kCLLocationAccuracyBest;
[locManager startUpdatingLocation];
But I need to get below the latitude and longitude user. Obviously, trying to obtain locManager.coordinate just below the startUpdatingLocation my application crashes.
How can I make a condition to perform a process after the user allows sharing of location and it has been found?
Thanks in advance.
After calling startUpdatingLocation it can take some time until you get a location. The system first checks if location services are enabled for the app and asks the user to allow location services. The GPS hardware first needs to be turned on. It takes some time until it can fix the position.
So you should update the UI to show the user that there is something going on in the background. Consider using a UIActivityIndicatorView and maybe set userInteractionEnabled of the view to NO. Also it is a good practice to give the user the option to cancel the operation.
In your delegate you must implement these 2 methods:
– locationManager:didUpdateToLocation:fromLocation:
– locationManager:didFailWithError:
Here you can remove the UIActivityIndicatorView and reenable user interaction.
In case of success you can use the coordinate of the CLLocationManager to do whatever you want. In case of failure show an error alert.
Call stopUpdatingLocation when you don't need location services anymore to save battery.