0

I have an IOS 8-only app in which I want to use location services to obtain the device's latitude and longitude. I think I have everything implemented correctly, but the app never asks the user if it's okay to use location services, the CLAuthorizationStatus never changes from kCLAuthorizationStatusNotDetermined and the CLLocationManager delegate method

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading 

never gets called.

Here's the CLLocationManager defined in the interface file for the viewcontroller:

__strong CLLocationManager *locationManager;

Here's the code in the viewcontroller:

- (void)viewDidLoad{
    [super viewDidLoad];

    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
    if (status == kCLAuthorizationStatusNotDetermined) {
        [locationManager requestWhenInUseAuthorization];
    }
    [locationManager startUpdatingLocation];
    //... other viewDidLoad code
}

The Info.plist file for the app has an entry for NSLocationWhenInUseUsageDescription.

Debug tracing shows that the line

[locationManager requestWhenInUseAuthorization];

gets executed, but the dialog requesting the user to okay location services does not appear.

Location services in this app worked correctly under iOS 7 - I'm clearly doing something wrong, or not doing something I need to do to make it work in iOS 8. But I have dug around looking for insights, and it looks to me as if I'm doing everything correctly.

Any ideas and/or suggestions? Thanks in advance.

johnz
  • 489
  • 2
  • 17

3 Answers3

0

You're pretty close. Let me see if I can help. For iOS 8, you need to first call:

[self.locationManager requestWhenInUseAuthorization];

I recommend putting this right after your line of code that says: locationManager.delegate = self; (and then delete everything else in your viewDidLoad method). This is different from iOS 7 due to all the new privacy stuff. Don't call startUpdatingLocation until after this method's delegate callback is called. If you read the docs, you'll see that the method requestWhenInUseAuthorization, after finishing, calls its callback method:

locationManager:didChangeAuthorizationStatus:

Within the callback method, check the status and take action depending on what that value is. This is what my method looks like:

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    if(status == kCLAuthorizationStatusAuthorized || status == kCLAuthorizationStatusAuthorizedWhenInUse)
    {
        [self.locationManager startUpdatingLocation];
    }
}

Keep in mind that you won't see the alert asking if the app can use your location every time. After you've seen it once, it won't show again. The user can then control whether or not the app can access his / her location in the settings app on the device.

Brian Sachetta
  • 3,319
  • 2
  • 34
  • 45
  • Ran into a problem with that last comment...here's what I wanted to add: Thanks for your response, Brian. I do call the 'locationManager:requestWhenInUseAuthorization' method, and the CLLocationManager does indeed call the `locationManager:didChangeAuthorizationStatus` delegate method, but the status is always kCLAuthorizationStatusNotDetermined. Moving the `locationManager: startUpdatingLocation` method to the `didChangeAuthorizationStatus` didn't seem to change anything: the alert asking the user for permission to use location services never appears. – johnz Mar 27 '15 at 14:52
  • Ok that's interesting. Do you have location services enabled on the device? You can check either by going into the settings app on the device or seeing what this method returns: [CLLocationManager locationServicesEnabled] – Brian Sachetta Mar 27 '15 at 14:59
  • Location services are enabled, showing both in Settings and the return from `CLLocationManager locationServicesEnabled.` I have little doubt that this will be one of those "slap the forehead and yell 'Why Didn't I think of that long ago?'" answers, but for now, it's really got me buffaloed. Thanks for your help, Brian. – johnz Mar 28 '15 at 00:49
  • Yeah this is a tricky one. The only other thing I can think of is to move the call of requestWhenInUseAuthorization to viewWillAppear or viewDidAppear. Sometimes manipulating the view hierarchy in viewDidLoad messes things up. – Brian Sachetta Mar 28 '15 at 13:05
  • No luck with moving the code around, unfortunately. A probably non-related fact is that the location manager does provide heading information correctly. I assume that the permission required of the user applies only to location, not heading. Although I always seem to get better answers on Stack Overflow, I'll post this on the Developer forum and hope that an Apple person with knowledge of CLLocationManager takes pity on me... Thanks again, Brian. – johnz Mar 28 '15 at 14:37
  • No problem. Kindly update this thread if you come up with a solution. I will do the same. – Brian Sachetta Mar 28 '15 at 17:55
  • No answer yet, but an intriguing clue... I deleted the app from the device and re-loaded it from Xcode. Same behavior, but there is no entry for the app in the Settings/Privacy/Location Services page. The app still reports that location services are enabled. – johnz Mar 29 '15 at 17:57
  • I believe that makes sense. The location services enabled call just returns whether or not the device has location services enabled or not. The authorization status is what tells you whether your app is allowed to access said location. Since the app keeps failing to prompt the user for access to the location, this makes sense. – Brian Sachetta Mar 29 '15 at 18:41
  • One thing I'd try messing with is asking for access to the location"always" as well as "when in use." It might possibly shed some light on what's happening. You'd have to add an entry for it into your .plist like you did for the "when in use" one. – Brian Sachetta Mar 29 '15 at 18:43
  • I found the solution, though I don't know why. I realized I had duplicate info.plists in different folders of the app's folder. The two info.plists look to be identical, both with the NSLocationWhenInUseUsageDescription entry. But when I removed the one not referred to in the app's navigation window, the app started to work as advertised: got the request alert, allowed location services and the heading started to update. No idea why an identical duplicate info.plist, not included in the app's build, would cause that problem, but it seems to be the case. Many thanks again, Brian – johnz Mar 30 '15 at 03:53
  • Hey sometimes solutions are whacky. Glad to hear you've got an answer! – Brian Sachetta Mar 30 '15 at 13:09
0

first thing first, check for CLLocationManagerDelegate define or not then, do something like below.

NSString *systenversion=[[UIDevice currentDevice] systemVersion];

    if ([systenversion integerValue]>=8.0) {

        if ([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
            [locationManager requestAlwaysAuthorization];
        }
    }
    [locationManager startUpdatingLocation];

and the delegates,

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog(@"didFailWithError: %@", error);
    UIAlertView *errorAlert = [[UIAlertView alloc]
                               initWithTitle:@"Error" message:@"Failed to Get Your Location" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [errorAlert show];
}

- (void)locationManager:(CLLocationManager*)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status) {
        case kCLAuthorizationStatusNotDetermined: {
            NSLog(@"User still thinking..");
        } break;
        case kCLAuthorizationStatusDenied: {
            NSLog(@"User hates you");
        } break;
        case kCLAuthorizationStatusAuthorizedWhenInUse:
        case kCLAuthorizationStatusAuthorizedAlways: {
            [locationManager startUpdatingLocation];//Will update location immediately

            [self showAlertWithTitle:@"Success" andMessge:@"Success to get current location"];
        } break;
        default:
            break;
    }
}

hope this works. :)

Tejas Ardeshna
  • 4,343
  • 2
  • 20
  • 39
  • Thanks for the response, Tejas. See my response to Brian, above. The `locationManager:didChangeAuthorizationStatus` does get called by the locationManager, but he status is always Not Determined. The dialog asking for user permission to use location services never appears. – johnz Mar 27 '15 at 14:56
  • add one line in info.plist NSLocationWhenInUseUsageDescription = YES – Tejas Ardeshna Mar 28 '15 at 04:51
0

I found the solution, though I don't know why. I realized I had duplicate info.plists in different folders of the app's folder. The two info.plists look to be identical, both with the NSLocationWhenInUseUsageDescription entry. But when I removed the one not referred to in the app's navigation window, the app started to work as advertised: got the request alert, allowed location services and the heading started to update. No idea why an identical duplicate info.plist, not included in the app's build, would cause that problem, but it seems to be the case.

johnz
  • 489
  • 2
  • 17