Skip to content

Commit

Permalink
Add new API for getting the current state of location services
Browse files Browse the repository at this point in the history
Fixes #28, #29

- Add a new class method `locationServicesState` that returns the current state of system location services (based on the system settings, and the authorization status for this app)
- Deprecate the legacy API `locationServicesAvailable`, as the new API fully replaces it
- Minor internal refactoring to leverage the above changes

Thanks to @GabrielCartier, @KhanFu, @pronebird for the suggestions and help thinking this through.
  • Loading branch information
Tyler Fox authored and Tyler Fox committed Mar 18, 2015
1 parent f6c01f7 commit d160383
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 28 deletions.
18 changes: 15 additions & 3 deletions Source/INTULocationManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@

/**
An abstraction around CLLocationManager that provides a block-based asynchronous API for obtaining the device's location.
This class will automatically start and stop system location services as needed to conserve battery.
INTULocationManager automatically starts and stops system location services as needed to minimize battery drain.
*/
@interface INTULocationManager : NSObject

/** Returns YES if location services are enabled in the system settings, and the app has NOT been denied/restricted access. Returns NO otherwise. */
@property (nonatomic, readonly) BOOL locationServicesAvailable;
/** Returns the current state of location services for this app, based on the system settings and user authorization status. */
+ (INTULocationServicesState)locationServicesState;

/** Returns the singleton instance of this class. */
+ (instancetype)sharedInstance;
Expand Down Expand Up @@ -90,3 +90,15 @@
- (void)cancelLocationRequest:(INTULocationRequestID)requestID;

@end


/**
A category on INTULocationManager that exposes deprecated legacy APIs. These should no longer be used, and will be removed in a future release.
*/
@interface INTULocationManager (Deprecated)

/** DEPRECATED, will be removed in a future release. Please use +[INTULocationManager locationServicesState] instead.
Returns YES if location services are enabled in the system settings, and the app has NOT been denied/restricted access. Returns NO otherwise. */
@property (nonatomic, readonly) BOOL locationServicesAvailable __attribute__((deprecated));

@end
79 changes: 57 additions & 22 deletions Source/INTULocationManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,30 @@ @implementation INTULocationManager

static id _sharedInstance;

/**
Returns the current state of location services for this app, based on the system settings and user authorization status.
*/
+ (INTULocationServicesState)locationServicesState
{
if ([CLLocationManager locationServicesEnabled] == NO) {
return INTULocationServicesStateDisabled;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
return INTULocationServicesStateNotDetermined;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
return INTULocationServicesStateDenied;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted) {
return INTULocationServicesStateRestricted;
}

return INTULocationServicesStateAvailable;
}

/**
Returns the singleton instance of this class.
*/
+ (instancetype)sharedInstance
{
static dispatch_once_t _onceToken;
Expand All @@ -86,22 +110,6 @@ - (instancetype)init
return self;
}

/**
Returns YES if location services are enabled in the system settings, and the app has NOT been denied/restricted access. Returns NO otherwise.
Note that this method will return YES even if the authorization status has not yet been determined.
*/
- (BOOL)locationServicesAvailable
{
if ([CLLocationManager locationServicesEnabled] == NO) {
return NO;
} else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
return NO;
} else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted) {
return NO;
}
return YES;
}

/**
Asynchronously requests the current location of the device using location services.
Expand Down Expand Up @@ -239,8 +247,11 @@ - (void)cancelLocationRequest:(INTULocationRequestID)requestID
*/
- (void)addLocationRequest:(INTULocationRequest *)locationRequest
{
if ([self locationServicesAvailable] == NO) {
// Don't even bother trying to do anything since location services are off or the user has explcitly denied us permission to use them
INTULocationServicesState locationServicesState = [INTULocationManager locationServicesState];
if (locationServicesState == INTULocationServicesStateDisabled ||
locationServicesState == INTULocationServicesStateDenied ||
locationServicesState == INTULocationServicesStateRestricted) {
// No need to add this location request, because location services are turned off device-wide, or the user has denied this app permissions to use them
[self completeLocationRequest:locationRequest];
return;
}
Expand Down Expand Up @@ -428,16 +439,18 @@ - (void)processSubscriptionRequest:(INTULocationRequest *)locationRequest
*/
- (INTULocationStatus)statusForLocationRequest:(INTULocationRequest *)locationRequest
{
if ([CLLocationManager locationServicesEnabled] == NO) {
INTULocationServicesState locationServicesState = [INTULocationManager locationServicesState];

if (locationServicesState == INTULocationServicesStateDisabled) {
return INTULocationStatusServicesDisabled;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
else if (locationServicesState == INTULocationServicesStateNotDetermined) {
return INTULocationStatusServicesNotDetermined;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
else if (locationServicesState == INTULocationServicesStateDenied) {
return INTULocationStatusServicesDenied;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted) {
else if (locationServicesState == INTULocationServicesStateRestricted) {
return INTULocationStatusServicesRestricted;
}
else if (self.updateFailed) {
Expand Down Expand Up @@ -546,5 +559,27 @@ - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatu
}
}
}

#pragma mark Deprecated methods

/**
DEPRECATED, will be removed in a future release. Please use +[INTULocationManager locationServicesState] instead.
Returns YES if location services are enabled in the system settings, and the app has NOT been denied/restricted access. Returns NO otherwise.
Note that this method will return YES even if the authorization status has not yet been determined.
*/
- (BOOL)locationServicesAvailable
{
if ([CLLocationManager locationServicesEnabled] == NO) {
return NO;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
return NO;
}
else if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted) {
return NO;
}

return YES;
}

@end
21 changes: 18 additions & 3 deletions Source/INTULocationRequestDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ static const CGFloat kINTUUpdateTimeStaleThresholdBlock = 60.0; //
static const CGFloat kINTUUpdateTimeStaleThresholdHouse = 15.0; // in seconds
static const CGFloat kINTUUpdateTimeStaleThresholdRoom = 5.0; // in seconds

/** The possible states that location services can be in. */
typedef NS_ENUM(NSInteger, INTULocationServicesState) {
/** User has already granted this app permissions to access location services, and they are enabled and ready for use by this app.
Note: this state will be returned for both the "When In Use" and "Always" permission levels. */
INTULocationServicesStateAvailable,
/** User has not yet responded to the dialog that grants this app permission to access location services. */
INTULocationServicesStateNotDetermined,
/** User has explicitly denied this app permission to access location services. (The user can enable permissions again for this app from the system Settings app.) */
INTULocationServicesStateDenied,
/** User does not have ability to enable location services (e.g. parental controls, corporate policy, etc). */
INTULocationServicesStateRestricted,
/** User has turned off location services device-wide (for all apps) from the system Settings app. */
INTULocationServicesStateDisabled
};

/** A unique ID that corresponds to one location request. */
typedef NSInteger INTULocationRequestID;

Expand All @@ -64,7 +79,7 @@ typedef NS_ENUM(NSInteger, INTULocationAccuracy) {
INTULocationAccuracyRoom,
};

/** Statuses that can be passed to the completion block of a location request. */
/** A status that will be passed in to the completion block of a location request. */
typedef NS_ENUM(NSInteger, INTULocationStatus) {
// These statuses will accompany a valid location.
/** Got a location and desired accuracy level was achieved successfully. */
Expand All @@ -73,13 +88,13 @@ typedef NS_ENUM(NSInteger, INTULocationStatus) {
INTULocationStatusTimedOut,

// These statuses indicate some sort of error, and will accompany a nil location.
/** User has not responded to the permissions dialog. */
/** User has not yet responded to the dialog that grants this app permission to access location services. */
INTULocationStatusServicesNotDetermined,
/** User has explicitly denied this app permission to access location services. */
INTULocationStatusServicesDenied,
/** User does not have ability to enable location services (e.g. parental controls, corporate policy, etc). */
INTULocationStatusServicesRestricted,
/** User has turned off device-wide location services from system settings. */
/** User has turned off location services device-wide (for all apps) from the system Settings app. */
INTULocationStatusServicesDisabled,
/** An error occurred while using the system location services. */
INTULocationStatusError
Expand Down

0 comments on commit d160383

Please sign in to comment.