I am trying to get the user\'s current location using the following code, but it doesn\'t work. I have added both NSLocationWhenInUseUsageDescription
key and
The issue here is that on macOS prior to 10.15 one does not explicitly call to request location access like one does on iOS. The user permission prompt is automatically presented when you call startUpdatingLocation()
.
In your code above execution never gets to that call because your function startReceivingLocationChanges
always returns in the first statement where it checks current status (which will be "status not yet determined" most likely). So it never gets to the startUpdatingLocation()
call further down in that function and thus never prompts the user to allow location reporting.
In macOS 10.15 requestAlwaysAuthorization()
is available, but doesn't seem to be required if you just need to use location when your app is in use.
Also, on macOS .authorized
seems to be preferred over .authorizedAlways
(documented to be synonyms), though with the addition of the requestAlwaysAuthorization()
function in 10.15 they may change this (though the documentation has not been updated to indicate this having happened at the time of this answer).
If you are not calling requestAlwayAuthorization()
then it seems that only the NSLocationWhenInUseUsageDescription
info.plist key is needed.
Additionally, it is necessary to set the "Location" checkbox in "Hardened Runtime" under "Signing & Capabilities" for the macOS application project. This is required on Xcode 11.2.1 on macOS 10.14.6 where I'm testing this. Older setups, or ones not adopting the hardened runtime (now the default), may have to set this in a different location in the project build settings.
Here's the source for a NSViewController subclass that checks for location manager and the current location successfully in Xcode 11.2.1 on macOS 10.14.6:
import Cocoa
import CoreLocation
class ViewController: NSViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
print("starting location update requests")
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
print("location manager auth status changed to:" )
switch status {
case .restricted:
print("status restricted")
case .denied:
print("status denied")
case .authorized:
print("status authorized")
let location = locationManager.location
print("location: \(String(describing: location))")
case .notDetermined:
print("status not yet determined")
default:
print("unknown state: \(status)")
}
}
func locationManager(_ manager: CLLocationManager,
didFailWithError error: Error) {
print( "location manager failed with error \(error)" )
}
}
This works for me on macOS if I say yes to the "enable location services" prompt when I launch the app the first time.
Console output is (slightly obfuscated):
location manager auth status changed to: status not yet determined location manager auth status changed to: status authorized location: Optional(<+4X.48,-12X.62632228> +/- 65.00m (speed -1.00 mps / course -1.00) @ 11/15/19, 10:24:30 AM Pacific Standard Time)
Steps to make this sample:
NSLocationWhenInUseUsageDescription
key to the info.plist