56

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?

hasan
  • 23,815
  • 10
  • 63
  • 101
Melih Mucuk
  • 6,988
  • 6
  • 37
  • 56

8 Answers8

139

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];
    }
}
Boris
  • 316
  • 2
  • 10
Melih Mucuk
  • 6,988
  • 6
  • 37
  • 56
  • 1
    Do check on kCLAuthorizationStatusRestricted too if you need to inform user to request someone to enable it for them. – Nur Iman Izam Mar 03 '14 at 18:56
  • If you misconfigure your application (e.g. misspell NSLocationsAlwaysUsageDescription) then locationServicesEnabled still seems to return true. Checking locationManager:didChangeAuthorizationStatus: seems to detect that problem. – Gerard May 09 '15 at 05:14
  • @Melih Mucuk shouldn't the ```NSLog``` statement be in an else statement after the ```if```` statement called ```authorizationStatus```? – Supertecnoboff May 24 '15 at 10:50
  • after a lot of investigation. I would recommend to display this message on a label. not using alert view. because, the are a lot of situations to test against(deleting app, reinstall it, disable location service in general or just for app. re-delete, reinstall ...). one of these cases causes to show your alert message and apple's alert message at the same time. your alert will be behind apples alert. which is a confusing and un-logical behaviour. I added an answer below. – hasan Sep 14 '15 at 08:43
  • its always return yes, i did not give allow location – karthikeyan Jul 14 '16 at 13:26
27

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
    }
}
Michal
  • 15,429
  • 10
  • 73
  • 104
swiftBoy
  • 35,607
  • 26
  • 136
  • 135
21
-(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.

swiftBoy
  • 35,607
  • 26
  • 136
  • 135
Virus
  • 390
  • 3
  • 6
13

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.)"
Sumit Mundra
  • 3,891
  • 16
  • 29
13

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
    }

}
Pooja Gupta
  • 313
  • 2
  • 9
  • Hi, In didUpdateLocation delegate the following line of code:- "NotificationCenter.default.post(name: NSNotification.Name.updateLocationNotification, object: nil)" Shows an error as:- "Type 'NSNotification.Name' has no member 'updateLocationNotification'" Please update it. – Ashutosh Mishra Mar 23 '21 at 04:54
11

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;
    }
}
hasan
  • 23,815
  • 10
  • 63
  • 101
5

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
}
Kakshil Shah
  • 3,466
  • 1
  • 17
  • 31
3

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
    }

}
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78