Checking location service permission on iOS

IosObjective CSwiftCllocationmanager

Ios Problem Overview


How can I check if location service is enabled for my app?

I have 2 storyboards and I want to check location service. If location service enabled for my app, I want to launch map storyboard with location. Otherwise, I want to launch another storyboard. How can I do programmatically?

Ios Solutions


Solution 1 - Ios

This is the correct.

if ([CLLocationManager locationServicesEnabled]){
            
    NSLog(@"Location Services Enabled");
            
    if ([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied){
        alert = [[UIAlertView alloc] initWithTitle:@"App Permission Denied"     
                                           message:@"To re-enable, please go to Settings and turn on Location Service for this app." 
                                          delegate:nil 
                                 cancelButtonTitle:@"OK" 
                                 otherButtonTitles:nil];
        [alert show];
    }
}

Solution 2 - Ios

Tested on iOS 9.2

For getting location updates we should always check

  • Location services enabled on user's iOS Device and
  • Location services enabled for particular app

and launching user on correct settings screen to enable

Launch iOS Device Location Settings page

Step.1 Go to Project settings --> Info --> URL Types --> Add New URL Schemes

enter image description here

Step.2 Use below code to launch direct phone's location settings page: (Note: The URL Scheme is different in iOS 10+, we check the version as stated here)

 #define SYSTEM_VERSION_LESS_THAN(v)  ([[[UIDevice 
 currentDevice] systemVersion] compare:v options:NSNumericSearch] == 
 NSOrderedAscending)

 //Usage
NSString* url = SYSTEM_VERSION_LESS_THAN(@"10.0") ? @"prefs:root=LOCATION_SERVICES" : @"App-Prefs:root=Privacy&path=LOCATION";
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString: url]];

enter image description here

Launch Application Location Settings page

Use below code to launch direct application's location settings page

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];

enter image description here

Here is the full code example :

#define SYSTEM_VERSION_LESS_THAN(v)  ([[[UIDevice 
 currentDevice] systemVersion] compare:v options:NSNumericSearch] == 
 NSOrderedAscending)


CLLocationManager *locationManager;

-(void) checkLocationServicesAndStartUpdates
{
    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    
    if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])
    {
        [locationManager requestWhenInUseAuthorization];
    }
    
    //Checking authorization status
    if (![CLLocationManager locationServicesEnabled] && [CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied)
    {
        
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Location Services Disabled!"
                                                            message:@"Please enable Location Based Services for better results! We promise to keep your location private"
                                                           delegate:self
                                                  cancelButtonTitle:@"Settings"
                                                  otherButtonTitles:@"Cancel", nil];
        
        //TODO if user has not given permission to device
        if (![CLLocationManager locationServicesEnabled])
        {
            alertView.tag = 100;
        }
        //TODO if user has not given permission to particular app
        else
        {
            alertView.tag = 200;
        }
        
        [alertView show];
        
        return;
    }
    else
    {
        //Location Services Enabled, let's start location updates
        [locationManager startUpdatingLocation];
    }
}

Handle the user click respone, and launch correct location settings

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    
    if(buttonIndex == 0)//Settings button pressed
    {
        if (alertView.tag == 100)
        {
            //This will open ios devices location settings
            NSString* url = SYSTEM_VERSION_LESS_THAN(@"10.0") ? @"prefs:root=LOCATION_SERVICES" : @"App-Prefs:root=Privacy&path=LOCATION";
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString: url]];
        }
        else if (alertView.tag == 200)
        {
            //This will opne particular app location settings
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
        }
    }
    else if(buttonIndex == 1)//Cancel button pressed.
    {
        //TODO for cancel
    }
}

Solution 3 - Ios

-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{

    NSLog(@"%@",error.userInfo);
    if([CLLocationManager locationServicesEnabled]){
        
        NSLog(@"Location Services Enabled");
        
        if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied){
         UIAlertView    *alert = [[UIAlertView alloc] initWithTitle:@"App Permission Denied"
                                               message:@"To re-enable, please go to Settings and turn on Location Service for this app."
                                              delegate:nil
                                     cancelButtonTitle:@"OK"
                                     otherButtonTitles:nil];
            [alert show];
        }
    }
 }

Reason behind this, this method will call when your service will be disable the location service. this code is useful for me.

Solution 4 - Ios

Check CLLocationManager's locationServicesEnabled property to check the system-wide availability. Use your CLLocationManagerDelegate's locationManager: didFailWithError: method and check for a kCLErrorDenied error to see if the user denied location services.

BOOL locationAllowed = [CLLocationManager locationServicesEnabled];
 if (!locationAllowed) 
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Location Service Disabled" 
                                                        message:@"To re-enable, please go to Settings and turn on Location Service for this app." 
                                                       delegate:nil 
                                              cancelButtonTitle:@"OK" 
                                              otherButtonTitles:nil];
        [alert show];
        [alert release];
}

for your app use this code

- (void)viewDidLoad
{
    locationManager = [[CLLocationManager alloc] init];
  
    locationManager.delegate = self;
    
    locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
   
    // Set a movement threshold for new events.
    
    locationManager.distanceFilter = 500;
 
    [locationManager startUpdatingLocation];
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
}

- (void)locationManager:(CLLocationManager *)manager

     didUpdateLocations:(NSArray *)locations {
    
    // If it's a relatively recent event, turn off updates to save power
    
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{

    NSLog(@"%@",error);
}

if location service disable for your app then its give you error

Error Domain=kCLErrorDomain Code=1 "The operation couldn’t be completed. (kCLErrorDomain error 1.)"

Solution 5 - Ios

Updated in Latest Swift 5.0, Xcode 11.2.1

import UIKit
import CoreLocation

>User constants

struct UserConstants {
    static let latitude = "latitude"
    static let longitude = "longitude"
    static let lastKnownLatitude = "lastKnownLatitude"
    static let lastKnownLongitude = "lastKnownLongitude"
}

> Location Manager Delegate for monitoring location changes

@objc protocol LocationManagerDelegate {
    @objc optional func getLocation(location: CLLocation)
}

class LocationHelper: NSObject, CLLocationManagerDelegate {
    
    weak var locationManagerDelegate: LocationManagerDelegate?
    var isLocationfetched: Bool = false
    var lastKnownLocation: CLLocation? {
        get {
            let latitude = UserDefaults.standard.double(forKey: UserConstants.lastKnownLatitude)
            let longitude = UserDefaults.standard.double(forKey: UserConstants.lastKnownLongitude)
            
            if latitude.isZero || longitude.isZero {
                return nil
            }
            return CLLocation(latitude: latitude, longitude: longitude)
        }
        set {
            UserDefaults.standard.set(newValue?.coordinate.latitude ?? 0, forKey: UserConstants.lastKnownLatitude)
            UserDefaults.standard.set(newValue?.coordinate.longitude ?? 0, forKey: UserConstants.lastKnownLongitude)
            UserDefaults.standard.synchronize()
        }
    }
    
    struct SharedInstance {
        static let instance = LocationHelper()
    }
    
    class var shared: LocationHelper {
        return SharedInstance.instance
    }
    
    enum Request {
        case requestWhenInUseAuthorization
        case requestAlwaysAuthorization
    }
    
    var clLocationManager = CLLocationManager()
    
    func setAccuracy(clLocationAccuracy: CLLocationAccuracy) {
        clLocationManager.desiredAccuracy = clLocationAccuracy
    }
    
    var isLocationEnable: Bool = false {
        didSet {
            if !isLocationEnable {
                lastKnownLocation = nil
            }
        }
    }
    

> Location updates with authorization check

    func startUpdatingLocation() {
        isLocationfetched = false
        if CLLocationManager.locationServicesEnabled() {
            switch CLLocationManager.authorizationStatus() {
            case .notDetermined:
                clLocationManager.delegate = self
                clLocationManager.requestWhenInUseAuthorization()
                clLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
                clLocationManager.startUpdatingLocation()
                isLocationEnable = true
            case .restricted, .denied:
                showLocationAccessAlert()
                isLocationEnable = false
            case .authorizedAlways, .authorizedWhenInUse:
                self.clLocationManager.delegate = self
                self.clLocationManager.startUpdatingLocation()
                isLocationEnable = true
            default:
                print("Invalid AuthorizationStatus")
            }
        } else {
            isLocationEnable = false
            showLocationAccessAlert()
        }
    }
    

>Show location alert if permission is not allowed

    func showLocationAccessAlert() {
        let alertController = UIAlertController(title: "Location Permission Required", message: "Please enable location permissions in settings.", preferredStyle: UIAlertController.Style.alert)
        let okAction = UIAlertAction(title: "settings", style: .default, handler: {(cAlertAction) in
            UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
        })
        let cancelAction = UIAlertAction(title: "cancel", style: UIAlertAction.Style.cancel)
        alertController.addAction(cancelAction)
        alertController.addAction(okAction)
        let appdelegate = UIApplication.shared.delegate as? AppDelegate
        appdelegate?.window?.rootViewController?.present(alertController, animated: true, completion: nil)
    }
    
    func stopUpdatingLocation() {
        self.clLocationManager.stopUpdatingLocation()
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if !isLocationfetched {
            isLocationfetched = true
            clLocationManager.startMonitoringSignificantLocationChanges()
            NotificationCenter.default.post(name: NSNotification.Name.updateLocationNotification, object: nil)
        }
        let userLocation = locations[0] as CLLocation
        self.lastKnownLocation = userLocation
        if let delegate = self.locationManagerDelegate {
            delegate.getLocation!(location: userLocation)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if (status == CLAuthorizationStatus.denied) {
            // The user denied authorization
            isLocationEnable = false
        } else if (status == CLAuthorizationStatus.authorizedWhenInUse) {
            // The user accepted authorization
            self.clLocationManager.delegate = self
            self.clLocationManager.startUpdatingLocation()
            isLocationEnable = true
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("\n error description for location updation:- \(error.localizedDescription)")
    }
    
}

>For testing above, just write these line of code in your controller,

LocationHelper.shared.locationManagerDelegate = self
LocationHelper.shared.startUpdatingLocation()

>LocationManagerDelegate Methods

extension ViewController: LocationManagerDelegate {
    
    func getLocation(location: CLLocation) {
        currentLocation = location.coordinate
    }
    
}

Solution 6 - Ios

After a lot of investigation. I would recommend to display this message on a label and not on an alert view. because, there are a lot of cases to test against(user disables location service in general or just for app. delete app, reinstall).

One of these cases causes your alert to show your message along with apple's alert message at the same time. your alert will be behind apple's alert. which is a confusing and un-logical behavior.

I recommend the following:

Swift 3:

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    
    switch status {
        case .notDetermined:
            Log.verbose("User still thinking granting location access!")
            manager.startUpdatingLocation() // this will access location automatically if user granted access manually. and will not show apple's request alert twice. (Tested)
        break
        
        case .denied:
            Log.verbose("User denied location access request!!")
            // show text on label
            label.text = "To re-enable, please go to Settings and turn on Location Service for this app."
            
            manager.stopUpdatingLocation()
            loadingView.stopLoading()
        break
        
        case .authorizedWhenInUse:
            // clear text
            label.text = ""
            manager.startUpdatingLocation() //Will update location immediately
        break
        
        case .authorizedAlways:
            // clear text
            label.text = ""
            manager.startUpdatingLocation() //Will update location immediately
        break
        default:
            break
    }
}

Objective-C:

- (void)locationManager:(CLLocationManager*)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    switch (status) {
        case kCLAuthorizationStatusNotDetermined: {
            DDLogVerbose(@"User still thinking granting location access!");
            [locationManager startUpdatingLocation]; // this will access location automatically if user granted access manually. and will not show apple's request alert twice. (Tested)
        } break;
        case kCLAuthorizationStatusDenied: {
            DDLogVerbose(@"User denied location access request!!");
            // show text on label
            label.text = @"To re-enable, please go to Settings and turn on Location Service for this app.";
            
            [locationManager stopUpdatingLocation];
            [loadingView stopLoading];
        } break;
        case kCLAuthorizationStatusAuthorizedWhenInUse:
        case kCLAuthorizationStatusAuthorizedAlways: {
            // clear text
            label.text = @"";
            [locationManager startUpdatingLocation]; //Will update location immediately
        } break;
        default:
            break;
    }
}

Solution 7 - Ios

The best way, handling all cases! ->

//First, checking if the location services are enabled
if(![CLLocationManager locationServicesEnabled]){
    [self showMessage:@"Please enable location services to detect location!" withTitle:@"Location not enabled"];
}
else if ([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied){
    //Now if the location is denied.
    UIAlertController *alertController = [UIAlertController
                                          alertControllerWithTitle:@"Enable location permission"
                                          message:@"To auto detect location, please enable location services for this app"
                                          preferredStyle:UIAlertControllerStyleAlert];
    
    alertController.view.tintColor = AppColor;
    UIAlertAction *cancelAction = [UIAlertAction
                                   actionWithTitle:@"Dismiss"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction *action)
                                   {
                                        NSLog(@"Cancel action");
                                   }];
    
    UIAlertAction *goToSettings = [UIAlertAction
                                actionWithTitle:@"Settings"
                                style:UIAlertActionStyleDefault
                                handler:^(UIAlertAction *action)
                                {
                                    //Simple way to open settings module
                                    NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                                    [[UIApplication sharedApplication] openURL:url];
                                }];
    
    [alertController addAction:cancelAction];
    [alertController addAction:goToSettings];
    [self presentViewController:alertController animated:YES completion:^{
        alertController.view.tintColor = AppColor;
    }];
}
else{
    //Do whatever you want here
}

Solution 8 - Ios


> Swift 3.0 & iOS 10 Solution:


self.locationManager?.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() && CLLocationManager.authorizationStatus() != CLAuthorizationStatus.denied {
            locationManager?.delegate = self
            locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
            locationManager?.distanceFilter = distanceFiler
            locationManager?.startUpdatingLocation()
        }else{
            let alertView = UIAlertView(title: "Location Services Disabled!", message: "Please enable Location Based Services for better results! We promise to keep your location private", delegate: self, cancelButtonTitle: "Settings", otherButtonTitles: "Cancel")
            alertView.delegate = self
            alertView.show()
            return
        }


@objc(alertView:clickedButtonAtIndex:) func alertView(_ alertView: UIAlertView, clickedButtonAt buttonIndex: Int) {
    if buttonIndex == 0 {
            if let url = URL(string: "App-Prefs:root=LOCATION_SERVICES") {
                UIApplication.shared.open(url, completionHandler: .none)
            }
    }
    else if buttonIndex == 1 {
        //TODO for cancel
    }
    
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMelih MucukView Question on Stackoverflow
Solution 1 - IosMelih MucukView Answer on Stackoverflow
Solution 2 - IosswiftBoyView Answer on Stackoverflow
Solution 3 - IosVirusView Answer on Stackoverflow
Solution 4 - IosSumit MundraView Answer on Stackoverflow
Solution 5 - IosPooja GuptaView Answer on Stackoverflow
Solution 6 - IoshasanView Answer on Stackoverflow
Solution 7 - IosKakshil ShahView Answer on Stackoverflow
Solution 8 - IosSourabh SharmaView Answer on Stackoverflow