25

I just upgraded to Xcode 14.0 and when I run our app on iOS 16 devices, calls to:

CLLocationManager.locationServicesEnabled()

Are returning the warning:

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

I'd need to make significant changes to my code if I have to wait for a failure/callback rather than just calling the CLLocationManager.locationServicesEnabled() method directly. This only seems to happen on iOS 16 devices. Any suggests on how to address this?

nurider
  • 1,555
  • 1
  • 18
  • 21
  • don't you ask for authorization if you don't have it? and if you do, that would be the same code as apple wants you to implement here (i.e. regardless of current authorization status, ask for it, and process an async response) – timbre timbre Sep 21 '22 at 18:36
  • 2
    @khjfquantumjj You know that `authorizationStatus` and `locationServicesEnabled()` are returning two entirely distinct statuses, right? – AlanSTACK Oct 12 '22 at 03:23
  • @AlanSTACK read the question. Based on warning the OP receives, it could be that he is trying to get `locationServicesEnabled` when user didn't authorize the access to location services, while authorization is a prerequisite to be able to obtain `locationServicesEnabled` status. – timbre timbre Oct 12 '22 at 20:42
  • 1
    None of the suggested solutions worked for me. I had to reconfigure my code to move this call off the main thread. I couldn't find another way around it. – nurider Nov 08 '22 at 17:45
  • 1
    @akjndklskver @AlanSTACK You can check `authorizationStatus` on a `CLLocationManager` instance without having permission. If the user has not granted permission, then the status will be `denied`. – bugloaf Dec 10 '22 at 17:51
  • @bugloaf You're wrong: The status might be denied because user disabled only for that app: 1) If you disable location services: `status = denied, locationServicesEnabled = false` 2) If you disable location services only for your app: `status = denied, locationServicesEnabled = true` 3) If you enable for both of them: `status = always/while using.., locationServicesEnabled = true` – yonivav Dec 26 '22 at 12:08

3 Answers3

24

Use a dispatch queue to move it off the main queue like so:

DispatchQueue.global().async {
      if CLLocationManager.locationServicesEnabled() {
      // your code here
      }
}
Yohst
  • 1,671
  • 18
  • 37
  • Two other options are to wrap in `Task.detached` or use `async let enabled = CLLocationManager.locationServicesEnabled()` when `await enabled` within a Task. Any comments which is preferred? – David Aug 03 '23 at 06:59
7

I faced the same issue and overcame it using Async/Await.

Wrap the CLLocationManager call in an async function.

func locationServicesEnabled() async -> Bool {
    CLLocationManager.locationServicesEnabled()
}

Then update the places where you use this function accordingly.

Task { [weak self] in

    if await self?.locationServicesEnabled() { 
        // Do something
    }
}
Marcos Curvello
  • 873
  • 13
  • 25
  • 6
    This still gives me the same warning. – akaakoz Nov 01 '22 at 11:06
  • 1
    This method can cause UI unresponsiveness if invoked on the main thread. Instead, consider waiting for the `-locationManagerDidChangeAuthorization:` callback and checking `authorizationStatus` first. – Zack117 Mar 01 '23 at 18:29
  • Yes, the example code above should consider that Tasks inherit the context in which they're instantiated. – Marcos Curvello Mar 02 '23 at 21:55
-1

In my case I had to separate both main.async and global().async to check some options in the background.

Edug
  • 1
  • 1
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 31 '23 at 13:46