iOS: App is not asking user's permission while

2020-01-25 14:17发布

问题:

I am trying to fetch user location in my iOS app. I have included corelocation framework in my project first. Then on a button click I am invoking the core location api as below. When I am trying to install this in a device, the core location never asks the user permission. When I try to fetch the location on button click, I am getting kCLAuthorizationStatusNotDetermined as the authorisationStatus. Please help me in this. I have no clue what is happening.

- (IBAction)fetchLessAccurateLocation:(id)sender {
    [self.txtLocation setText:@""];

    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
    locationManager.distanceFilter = 1000;

    if ([self shouldFetchUserLocation]) {
        [locationManager startUpdatingLocation];
    }
}

This is my shouldFetchUserLocation method:

-(BOOL)shouldFetchUserLocation{

    BOOL shouldFetchLocation= NO;

    if ([CLLocationManager locationServicesEnabled]) {
        switch ([CLLocationManager authorizationStatus]) {
            case kCLAuthorizationStatusAuthorized:
                shouldFetchLocation= YES;
                break;
            case kCLAuthorizationStatusDenied:
            {
                UIAlertView *alert= [[UIAlertView alloc]initWithTitle:@"Error" message:@"App level settings has been denied" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
                [alert show];
                alert= nil;
            }
                break;
            case kCLAuthorizationStatusNotDetermined:
            {
                UIAlertView *alert= [[UIAlertView alloc]initWithTitle:@"Error" message:@"The user is yet to provide the permission" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
                [alert show];
                alert= nil;
            }
                break;
            case kCLAuthorizationStatusRestricted:
            {
                UIAlertView *alert= [[UIAlertView alloc]initWithTitle:@"Error" message:@"The app is recstricted from using location services." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
                [alert show];
                alert= nil;
            }
                break;

            default:
                break;
        }
    }
    else{
        UIAlertView *alert= [[UIAlertView alloc]initWithTitle:@"Error" message:@"The location services seems to be disabled from the settings." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
        [alert show];
        alert= nil;
    }

    return shouldFetchLocation;
}

Here is my core location delegate method:

- (void)locationManager:(CLLocationManager *)manager
    didUpdateLocations:(NSArray *)locations __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0){

    NSLog(@"location fetched in delegate");

    CLLocation* location = [locations lastObject];
    NSDate* eventDate = location.timestamp;
    NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];

    if (abs(howRecent) < 15.0) {
        // If the event is recent, do something with it.
        NSLog(@"inside loop.... latitude %+.6f, longitude %+.6f\n",
              location.coordinate.latitude,
              location.coordinate.longitude);
    }
    NSLog(@"latitude %+.6f, longitude %+.6f\n",
          location.coordinate.latitude,
          location.coordinate.longitude);
    [self.txtLocation setText:[NSString stringWithFormat:@"\nlatitude: %+.6f \nlongitude:   %+.6f", location.coordinate.latitude, location.coordinate.longitude]];

    [locationManager stopUpdatingLocation];
    [locationManager stopMonitoringSignificantLocationChanges];

    if(locationManager!=nil){
        locationManager.delegate= nil;
        locationManager= nil;
    }
}

回答1:

I was facing the same issue, after re-installing my app it was returning kCLAuthorizationStatusNotDetermined whenever checking for [CLLocationManager authorizationStatus] and the app didn't even show up in Settings > Privacy > Location Services.

The authorization dialog that iOS prompts user to approve access to location services is triggered on [locationManager startUpdatingLocation] which in your case is never called (shouldFetchUserLocation will be always NO).

Miguel C.'s solution seems like a good fix, will try that.

Edit for iOS8.x

When iOS8 came it brought little change in the way CLLocationManager is used. As mentioned few times in other answers it requires additional step comparing to iOS7. Today I faced the issue myself and found this article (it's been referenced from multiple other questions but it completes my earlier answer). Hope it helps!



回答2:

iOS8 has got us major API changes with the LocationsServices

Assuming [CLLocationManager locationServicesEnabled] return YES,

With the First Launch of the iOS App [both iOS7 and iOS8] - locationMangers(CLLocationManager) authorizationStatus preset to

authorizationStatus(CLAuthorizationStatus) = kCLAuthorizationStatusNotDetermined

Prompting in iOS7+

Initiate the locationManger (CLLocationManager , Strong) and set the delegates(CLLocationManagerDelegate)

Now to prompt the user to use Locations Services and List the App under the Settings> Privacy> Locations Services its MUST to call any of the Locations Services Methods, its depends on the App requirement- for example if app is kind of one of the below

Locations Updates - [self.locationManager startUpdatingLocation]

RegionMonitoring - [self.locationManager startMonitoringForRegion:beaconRegion]

right after executions of the above method iOS will prompt user requesting to accept use of Locations Services in app and irrespective of user choice app will be listed under the Settings > Privacy > Locations Services.

Prompting in iOS8+

Its in same case with iOS8, with the first launch the App Locations Services

authorizationStatus(CLAuthorizationStatus) = kCLAuthorizationStatusNotDetermined

iOS 8 we got new methods for showing prompt to user

[self.locationManager requestAlwaysAuthorization] 
or
[self.locationManager requestWhenInUseAuthorization]

requestAlwaysAuthorization/requestWhenInUseAuthorization availbable from iOS8. if App deployment target is iOS7 then wrap this under if block to make sure in iOS7 this doesn't lead to app crash.

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

or

if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])
{
[self.locationManager requestWhenInUseAuthorization];
}

Very Important ###:

As per iOS8 its mandatory to include the string stating why app uses requestAlwaysAuthorization/requestWhenInUseAuthorization In info.plist include any of these property respective to the requirement of the app

for kCLAuthorizationStatusAuthorizedAlways include Key/Value(string Value) pair

NSLocationAlwaysUsageDescription = App use Locations service mode Always

for kCLAuthorizationStatusAuthorizedWhenInUse include Key/Value(string Value) pair

NSLocationWhenInUseUsageDescription = App use Locations service mode In Use 

Screenshot of info.plist (in case anybody is confused by this)



回答3:

Objective-C Follow the below intructions:

iOS-11 For iOS 11 have a look into this Answer: iOS 11 location access

Need to Add two Keys into plist and provide message as below image:

 1. NSLocationAlwaysAndWhenInUseUsageDescription 
 2. NSLocationWhenInUseUsageDescription

iOS-10 and below:

NSLocationWhenInUseUsageDescription

locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
if([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]){
    [locationManager requestWhenInUseAuthorization];
}else{
    [locationManager startUpdatingLocation];
} 

Delegat Methods

#pragma mark - Lolcation Update 
- (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:
        case kCLAuthorizationStatusRestricted:
        case kCLAuthorizationStatusDenied:
        {
            // do some error handling
        }
            break;
        default:{
            [locationManager startUpdatingLocation];
        }
            break;
    }
}
- (void)locationManager:(CLLocationManager *)manager
     didUpdateLocations:(NSArray *)locations
{
    CLLocation *location = [locations lastObject];
    userLatitude =  [NSString stringWithFormat:@"%f", location.coordinate.latitude] ;
    userLongitude =  [NSString stringWithFormat:@"%f",location.coordinate.longitude];
    [locationManager stopUpdatingLocation];
}

Swift Code

Follow the below instructions:

iOS-11 For iOS 11 have a look into this Answer: iOS 11 location access

Need to Add two Keys into plist and provide message as below image:

 1. NSLocationAlwaysAndWhenInUseUsageDescription 
 2. NSLocationWhenInUseUsageDescription

iOS-10 and below:

import CoreLocation
class ViewController: UIViewController ,CLLocationManagerDelegate {
var locationManager = CLLocationManager()

//MARK- Update Location 
func updateMyLocation(){
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
    if locationManager.respondsToSelector(#selector(CLLocationManager.requestWhenInUseAuthorization)){
       locationManager.requestWhenInUseAuthorization()
    }
    else{
        locationManager.startUpdatingLocation()
    }
}

Delegate Methods

//MARK: Location Update
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
    NSLog("Error to update location :%@",error)
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    switch status {
    case .NotDetermined: break
    case .Restricted: break
    case .Denied:
            NSLog("do some error handling")
        break
    default:
        locationManager.startUpdatingLocation()
    }
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
     let location = locations.last! as CLLocation
    var latitude = location.coordinate.latitude
    var longitude = location.coordinate.longitude

}


回答4:

You must have NSLocationWhenInUseUsageDescription added to you're info.plist. Its a bit weird but just add it as a string value with no text and then when you want location start with:

CLLocationManager * locationManager = [[CLLocationManager alloc] init];

[locationManager requestWhenInUseAuthorization];


回答5:

If you face this issue on iOS8, please use :requestWhenInUseAuthorization.



回答6:

In iOS 8 you need to do two extra things to get location working: Add a key to your Info.plist and request authorization from the location manager asking it to start. There are two Info.plist keys for the new location authorization. One or both of these keys is required. If neither of the keys are there, you can call startUpdatingLocation but the location manager won’t actually start. It won’t send a failure message to the delegate either (since it never started, it can’t fail). It will also fail if you add one or both of the keys but forget to explicitly request authorization.

So the first thing you need to do is to add one or both of the following keys to your Info.plist file:

NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription

Both of these keys take a string which is a description of why you need location services. You can enter a string like “Location is required to find out where you are” which, as in iOS 7, can be localized in the InfoPlist.strings file.

Next you need to request authorization for the corresponding location method, WhenInUse or Background. Use one of these calls:

[self.locationManager requestWhenInUseAuthorization]
[self.locationManager requestAlwaysAuthorization]

For more information.



回答7:

I have had the issue that Rashmi Ranjan mallick said in the comments few times since iOS 7 was release. I really think is one bug from iOS 7, they have a tons of them in the new version of iOS.

For me the issue was the same:

1.Open the app, never asked about location. iOS was not asking about permissons
2.I went to Settings->Privacy->Location services and my app was not there.
3.I was not able to change the location permissions for my app in any way.

One workaround for that is:

1.Kill your app.
2.Disable all location services with the main switch button.
3.Go to your app again.
4.Kill your app.
5.Enable the Location services again on the previous switch button.
6.If still doesn't appear on the list go again to your app, kill it again, and go back to the settings.
7.Now it should be there.

This worked for me and I hope this is useful.

UPDATE: if iOS 8 be sure you are calling requestWhenInUseAuthorization or requestAlwaysAuthorization

You may follow this code: :)

#ifdef __IPHONE_8_0
    if(NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1)
    {
        if([[Wave_SettingsObject sharedObject] backgroundLocationEnabled].boolValue == YES)
        {
            if(![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"])
            {
                NSLog(@"Info.plist does not contain NSLocationAlwaysUsageDescription key");
            }

            if ([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) //iOS 8+
            {
                [locationManager requestAlwaysAuthorization];
            }
        }
        else if([[Wave_SettingsObject sharedObject] isLocationEnabled].boolValue == YES)
        {
            if(![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"])
            {
                NSLog(@"Info.plist does not contain NSLocationWhenInUseUsageDescription key");
            }

            if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) //iOS 8+
            {
                [locationManager requestWhenInUseAuthorization];
            }

        }
    }
#endif


回答8:

Uninstall the app and try running it again.

If it didn't work, go to the settings and disable the authorization to that app. After that running it again to see if it asks permissions.

Or:

You can force the app start monitoring the location with some code like that:

self.locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];

in your delegate methods you can detect if there an error getting location and you can inform the user.

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    // Delegate of the location manager, when you have an error
    NSLog(@"didFailWithError: %@", error);

    UIAlertView *errorAlert = [[UIAlertView alloc]     initWithTitle:NSLocalizedString(@"application_name", nil) message:NSLocalizedString(@"location_error", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", nil) otherButtonTitles:nil];

    [errorAlert show];
}

If you have any questions tell me.



回答9:

Try adding

[locationManager startUpdatingLocation]

also make sure that location services is turned on.

EDIT:

Also try deleting the application and re-installing it. There may be a record of that the app is reading which is preventing it from asking for permission to use location.



回答10:

When prompting for location you must not forget those things (iOS 8+):

  1. Add this flag on your info.plist of the app : NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription depend if you want always access to location or only when app is in usage (the second is recommended if you don't need always access). This is the most important because it is new and weird.
  2. If you request always permission don't forget to add location updates as background mode on your target capabilities
  3. Make sure you are asking for the right permission when initialise location manager:

    -(void)initLocationManager{
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate=self;
    
        [locationManager requestAlwaysAuthorization]; // HERE ASK FOR THE RELEVANT
    }
    

For testing in simulator I suggest you to "Reset all content and settings" from it's options. That's the only way to get the exact same experience a new user of the app would get.



回答11:

I ran into this issue, implemented all of the things necessary including the pList keys and my app was still not requesting location.

Turns out the issue was that the locationManager variable cannot be a local - it needs to be an instance variable.

So instead of

-(void) updateLocation {
   CLLocationManager *locationManager = ...
}

it needs to be:

CLLocationManager *locationManager;

-(void) updateLocation {
  self.locationManager = ...
}


回答12:

I had a similar issue, but caused for a different reason. Adding it here in case it could help anyone else.

I stopped receiving the asking for authorization message, and since it was working before the requirements described in the other answers were already fulfilled: the key in the plist, checking authorization status and then asking for permission in ios8+.

In my case the issue was related to "Access Guide" mode. This is an app for a kiosk, and it only starts to range beacons once "Access Guide" is activated. But it seems that the ask for authorization messages aren't sent while in this mode. I could fix it just moving the ask for authorization procedure to the DidLoad of the first displayed view.

The same thing happens with the request to use the camera.