I have been having trouble with location services in iOS 11 for both "Allow while Use" and "Always Allow". Works without issue in iOS < 11. Followed this thread in trying to fix but still doesn't work. What am I missing? Thank you in advance.
- I have
UITabViewController
in my app and aUINavigationController
inside each tab. - I have a singleton LocationManager class. I'm setting my UINavigationController's RootViewController as delegate the ViewController's
viewWillAppear
to receive location updates and removing inviewWillDisappear
. - Now, When I launch the app, before the tab bar is created, I can see the location update is being called in the LocationManager Class.
- But when I add my UIViewController as delegate and give
startUpdatingLocation
I'm not receiving the location update in my UIVIewController. - Then I press Home button and exit the app. Again immediately launch the app and I get the location update in my delegate method.
I have added all three location authorization description in my Info.plist file.
Info.Plist:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Blah Blah Blah</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Blah Blah Blah</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Blah Blah Blah</string>
LocationController.m:
#import "LocationController.h"
//static int LOCATION_ACCESS_DENIED = 1;
//static int LOCATION_NETWORK_ISSUE = 2;
//static int LOCATION_UNKNOWN_ISSUE = 3;
enum {
LOCATION_ACCESS_DENIED = 1,
LOCATION_NETWORK_ISSUE = 2,
LOCATION_UNKNOWN_ISSUE = 3
};
static LocationController* sharedCLDelegate = nil;
@implementation LocationController
int distanceThreshold = 10.0; // in meters
@synthesize locationManager, currentLocation, locationObservers;
- (id)init
{
self = [super init];
if (self != nil) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = distanceThreshold;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
[self.locationManager startMonitoringSignificantLocationChanges];
self.locationManager.delegate = (id)self;
if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization]; //I tried both commenting this line and uncommenting this line. Didn't make any difference
}
if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
{
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startUpdatingLocation];
[self.locationManager startUpdatingHeading];
locationObservers = [[NSMutableArray alloc] init];
}
return self;
}
#pragma mark -
#pragma mark CLLocationManagerDelegate Methods
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(@"locationManager didUpdateLocations = %@",locations);
CLLocation *newLocation = [locations lastObject];
if (newLocation.horizontalAccuracy < 0) {
return;
}
currentLocation = newLocation;
for(id<LocationControllerDelegate> observer in self.locationObservers) {
if (observer) {
// CLLocation *newLocation = [locations lastObject];
// if (newLocation.horizontalAccuracy < 0) {
// return;
// }
// currentLocation = newLocation;
NSTimeInterval interval = [currentLocation.timestamp timeIntervalSinceNow];
//check against absolute value of the interval
if (fabs(interval)<30) {
[observer locationUpdate:currentLocation];
}
}
}
}
- (void)locationManager:(CLLocationManager*)manager
didFailWithError:(NSError*)error
{
NSLog(@"locationManager didFailWithError: %@", error);
for(id<LocationControllerDelegate> observer in self.locationObservers) {
if (observer) {
[observer failedToGetLocation:error];
}
}
switch (error.code) {
case kCLErrorDenied:
{
break;
}
case kCLErrorNetwork:
{
break;
}
default:
break;
}
}
#pragma mark - Singleton implementation in ARC
+ (LocationController *)sharedLocationInstance
{
static LocationController *sharedLocationControllerInstance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedLocationControllerInstance = [[self alloc] init];
});
return sharedLocationControllerInstance;
}
-(void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
NSLog(@"didChangeAuthorizationStatus = %i",status);
if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
[self.locationManager stopUpdatingLocation];
[self.locationManager startUpdatingLocation];
}
}
- (void) addLocationManagerDelegate:(id<LocationControllerDelegate>)delegate {
if (![self.locationObservers containsObject:delegate]) {
[self.locationObservers addObject:delegate];
}
[self.locationManager startUpdatingLocation];
}
- (void) removeLocationManagerDelegate:(id<LocationControllerDelegate>)delegate {
if ([self.locationObservers containsObject:delegate]) {
[self.locationObservers removeObject:delegate];
}
}
+ (id)allocWithZone:(NSZone *)zone {
@synchronized(self) {
if (sharedCLDelegate == nil) {
sharedCLDelegate = [super allocWithZone:zone];
return sharedCLDelegate; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
#pragma mark UIAlertViewDelegate Methods
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
switch (alertView.tag) {
case LOCATION_ACCESS_DENIED:
{
if (buttonIndex == 1) {
//[[UIApplication sharedApplication] openURL: [NSURL URLWithString: UIApplicationOpenSettingsURLString]];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=LOCATION_SERVICES"]];
}
}
break;
case LOCATION_NETWORK_ISSUE:
break;
case LOCATION_UNKNOWN_ISSUE:
break;
default:
break;
}
[alertView dismissWithClickedButtonIndex:buttonIndex animated:YES];
}
@end
LocationController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
// protocol for sending location updates to another view controller
@protocol LocationControllerDelegate
@required
- (void)locationUpdate:(CLLocation*)location;
- (void)failedToGetLocation:(NSError*)error;
@end
@interface LocationController : NSObject<CLLocationManagerDelegate,UIAlertViewDelegate>
@property (nonatomic, strong) CLLocationManager* locationManager;
@property (nonatomic, strong) CLLocation* currentLocation;
@property (strong, nonatomic) NSMutableArray *locationObservers;
+ (LocationController*)sharedLocationInstance; // Singleton method
- (void) addLocationManagerDelegate:(id<LocationControllerDelegate>) delegate;
- (void) removeLocationManagerDelegate:(id<LocationControllerDelegate>) delegate;
@end
ViewController.m
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"VC viewWillAppear");
[locationControllerInstance addLocationManagerDelegate:self];
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[LocationController sharedLocationInstance];
}