What is the reason for the CLLocationmanager.locationServicesEnabled() "invoked on main thread" warning?

I am looking into a piece of old code where the mentioned method is called.

+ (bool)isLocationServicesEnabled {
    return [CLLocationManager locationServicesEnabled];
}

I'm getting the classic "This method can cause UI unresponsiveness if invoked on the main thread. Instead, consider waiting for the -locationManagerDidChangeAuthorization: callback and checking authorizationStatus first."

I have 2 questions:

  1. What is that error about, really? The locationServicesEnabled() has nothing to do with authorisation, it's just about the "location services" settings global on-off switch? (the authorisation check is .authorizationStatus)

  2. I don't understand why that call is such a big issue? It's just a setting? Why would that be so costly?

Thankful for pointers! Have a good one

Answered by Engineer in 802408022

locationServicesEnabled() has everything to do with authorization. If the user disables this setting, your app will not able to receive authorization, and if it has been authorized before, it will lose that authorization.

You only need to check this if your app cares and can do something about why it does not have authorization. You can always just check .authorizationStatus and decide on your next steps if it is not important to know 'why'.

It may seem like "just a setting", but it is a synchronous call that needs to go out of your process to check that setting, and that is not an atomic action. Meaning, your main thread will block while it is waiting for a response. Whether it is a big issue or not depends on your app. It may matter to you if you are playing video, streaming real time data, or gaming. Or you may not care if your app mostly sits idle at your UI. Hence it's a warning you may heed or ignore.


Argun Tekant /  DTS Engineer / Core Technologies

locationServicesEnabled() has everything to do with authorization. If the user disables this setting, your app will not able to receive authorization, and if it has been authorized before, it will lose that authorization.

You only need to check this if your app cares and can do something about why it does not have authorization. You can always just check .authorizationStatus and decide on your next steps if it is not important to know 'why'.

It may seem like "just a setting", but it is a synchronous call that needs to go out of your process to check that setting, and that is not an atomic action. Meaning, your main thread will block while it is waiting for a response. Whether it is a big issue or not depends on your app. It may matter to you if you are playing video, streaming real time data, or gaming. Or you may not care if your app mostly sits idle at your UI. Hence it's a warning you may heed or ignore.


Argun Tekant /  DTS Engineer / Core Technologies

Hmm. It feels very scary to argue with an Apple engineer, but I have to make sure I'm not being unclear.

From Apple's doc:

CLLocationManager.LocationServicesEnabled():

Returns a Boolean value indicating whether location services are enabled on the device.

CLLocationManager.authorizationStatus

The current authorization status for the app

From the documentation, it seems pretty clear.

Please consider my short code snippet at the bottom. If I go into settings/privacy and turn the "location services" global checkbox back and forth, the locationServicesEnabled method returns true/false

My authorisation status does not change, and when I turn the global setting back I still have the authorisation status I had before.


This is why the error message seems strange to me. Saying that I should look at the authorizationstatus-callback to know whether the user has enabled location services globally is to me confusing two different things?

EDIT: To be super clear, I have the didChangeAuthorizationStatus implemented since a long time. It is called when you click around on the "always"; "in use" etc. options for my app. It is not called when I turn location services on and off, which is the call that Xcode is warning me about.

Snippet:

CLLocationManager *man = [[CLLocationManager alloc] init];
    
    //Returns a Boolean value indicating whether location services are enabled on the device.
    BOOL enabled = man.locationServicesEnabled;
    
    //The current authorization status for the app.
    CLAuthorizationStatus stat = man.authorizationStatus;
    
    [DialogUtils showBannerWithHeader:@"CHECK" message:[NSString stringWithFormat:@"Enabled? %i AuthStatus: %i", enabled, stat] type:MessageTypeINFO];

While you are free to argue with me as much as you wish, it is going to be quite fruitless to argue with Xcode over semantics.

This message (to be clear it is a Warning, not an Error) is simply trying to help you by pointing out that it may block your main thread, for reasons I explained. Neither is this message new.

Your opinions on whether this message is less helpful than we thought, or how it can be explained better will be best done on the Feedback Assistant

Right now, the warning exists, the reasoning behind is clear, and like I said before, whether to ignore it or not will depend on how critical a hiccup on the main thread will be for your app.


Argun Tekant /  DTS Engineer / Core Technologies

Accepted Answer

But it's not semantics or 'my opinions' - I'ts wrong. Xcode highlights the line locationServicesEnabled() and suggests I should instead wait for locationManagerDidChangeAuthorization which does not do the same thing.

Your answer "locationServicesEnabled() has everything to do with authorization" is simply incorrect and it's unfortunate that it becomes a highlighted answer next to my original post.

I want the warning to go away, but the remedy suggested by Xcode will not solve the problem.

I suppose I do agree that it's fruitless to argue any more. :) I'll try and file a support ticket.

didChangeAuthorizationStatus ... is not called when I turn location services on and off

That surprised me, so I looked at my code, and at the docs. There seem to be two CLLocationManagerDelegate methods, locationManagerDidChangeAuthorization: and locationManager: didChangeAuthorizationStatus:. The latter is deprecated and only briefly documented. The former is specifically documented to be called when the global location services setting is changed by the user:

https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/locationmanagerdidchangeauthorization(_:)?language=objc

Events that Cause Authorization Status Changes

...

Turn location services on or off globally in Settings > Privacy > Location Services.

So... which of those methods have you implemented? And are you sure that it's not called for you? If so, "file a bug" ha ha ha.

What is the reason for the CLLocationmanager.locationServicesEnabled() "invoked on main thread" warning?
 
 
Q