Xcode warning when using MapKit and CoreLocation

2020-01-29 21:40发布

问题:

I'm attempting to use implement an instance of MKMapView, use CoreLocation to track the users location, and then zoom in to where they are.

I only want to track the user's location when I'm in the foreground. Since my app is targeted for iOS8, I have a plist entry for the key NSLocationWhenInUseUsageDescription.

When I run the app for the first time, the app appropriately asks if it can access my location. After I click 'Allow', I then receive the following warning from Xcode:

Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.

...which is a bit confusing, as I am in fact calling requestWhenInUseAuthorization, as can be seen in my code below:

@property (strong, nonatomic) IBOutlet MKMapView *mapView;
@property(nonatomic, retain) CLLocationManager *locationManager;

@end

@implementation MapView

- (void)viewDidLoad {
    [super viewDidLoad];
    [self locationManager];
    [self updateLocation];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    self.locationManager = nil;
}

- (CLLocationManager *)locationManager {
    //We only want to get the location when the app is in the foreground
    [_locationManager requestWhenInUseAuthorization];
    if (!_locationManager) {
        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    }
    return _locationManager;
}

- (void)updateLocation {
    _mapView.userTrackingMode = YES;
    [self.locationManager startUpdatingLocation];
}

Does anyone have any insight into why this warning would be occurring?

回答1:

You are calling requestWhenInUseAuthorization, that is true. But are you waiting until you get authorization? No, you are not. You (as the user) are tapping Allow, but that's happening too late: your code has already continued, going straight on to tell the map view to start tracking the user's location.

Just look at the docs on requestWhenInUseAuthorization:

When the current authorization status is kCLAuthorizationStatusNotDetermined, this method runs asynchronously

Get that? Runs asynchronously. That means that asking for permission happens in the background on another thread.

And the docs go on to say:

After the status is determined, the location manager delivers the results to the delegate’s locationManager:didChangeAuthorizationStatus: method

So, implement that method. If you have just obtained permission, that is the signal that you can start using the location manager.

Also, you are missing an important step: you are not checking what the status actually is. You should only be asking for authorization if the status is undetermined. If the status is restricted or denied, you must not use the location manager at all; and if the status is granted, there is no point asking for authorization again.

So, just to sum up, your logical flowchart should be:

  • Check status.

  • Is the status Restricted or Denied? Stop. You cannot use get location updates or do location on a map.

  • Is the status Granted? Proceed to get location updates or do location on a map.

  • Is the status Undetermined? Request authorization and stop. Treat locationManager:didChangeAuthorizationStatus: as the completion handler for your authorization request. At that point, go back to start of the flowchart!