I’m trying to build a simple macOS command-line tool in Objective C that merely gets the current location via a CLLocationManager
. However, none of my delegate’s methods are ever called and my MacBook never prompts me to grant the app location access.
Here’s the core of my code:
bool
getLocation(MyDelegate *delegate)
{
CLLocationManager *manager;
if ( ![CLLocationManager locationServicesEnabled] ) {
NSLog(@"Location services are disabled");
return false;
}
manager = [CLLocationManager new];
[manager setDelegate:delegate];
[manager setDesiredAccuracy:kCLLocationAccuracyBest];
[manager requestAlwaysAuthorization];
NSLog(@"Requesting location");
[manager requestLocation];
NSLog(@"Waiting for delegate to be ready");
if ( ![delegate waitToBeReady:10] ) {
NSLog(@"Timed out");
return false;
}
return [delegate authorized];
}
MyDelegate.h:
typedef void (^Callback)(CLLocation *result, NSString *error);
@interface MyDelegate : NSObject <CLLocationManagerDelegate>
- (instancetype)initWithCallback:(Callback)callback;
- (bool)waitToBeReady:(unsigned int)timeout;
- (bool)authorized;
@end
MyDelegate.m:
@implementation MyDelegate {
Callback _callback;
dispatch_semaphore_t _ready;
bool _authorized;
}
- (instancetype)initWithCallback:(Callback)callback
{
self = [super init];
if ( self ) {
_callback = callback;
_ready = dispatch_semaphore_create(0);
_authorized = false;
}
return self;
}
- (bool)waitToBeReady:(unsigned int)timeout
{
dispatch_time_t deadline;
deadline = (timeout == 0)? DISPATCH_TIME_FOREVER : dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC);
return dispatch_semaphore_wait(_ready, deadline) == 0;
}
- (bool)authorized
{
return _authorized;
}
- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager
{
switch ( [manager authorizationStatus] ) {
case kCLAuthorizationStatusNotDetermined:
return;
case kCLAuthorizationStatusRestricted:
case kCLAuthorizationStatusDenied:
NSLog(@"Location authorization denied");
break;
case kCLAuthorizationStatusAuthorizedAlways:
NSLog(@"Location authorization granted");
_authorized = true;
break;
}
dispatch_semaphore_signal(_ready);
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
_callback([locations lastObject], nil);
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *error)
{
_callback(nil, [error localizedDesciption]);
}
Info.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSLocationAlwaysUsageDescription</key>
<string>We need your location to provide better services.</string>
</dict>
</plist>
I see the log message, “Waiting for delegate to be ready”, but it just times out. As mentioned above, none of my delegate’s methods are ever called and I never get prompted to grant the tool access.