Applications use location data for a wide variety of purposes, ranging from social networking to turn-by-turn navigation services. They get location data by using the classes of the Core Location framework. This framework provides several services that you can use to get and monitor the device’s current location:
The significant-change location service provides a low-power way to get the current location and be notified of changes to that location. (iOS 4.0 and later).
The standard location service offers a more configurable way to get the current location.
Region monitoring lets you monitor boundary crossings for a defined area. (iOS 4.0 and later).
To use the features of the Core Location framework, you must link your application to CoreLocation.framework
in your Xcode project. To access the classes and headers of the framework, include an #import <CoreLocation/CoreLocation.h>
statement at the top of any relevant source files.
For general information about the classes of the Core Location framework, see Core Location Framework Reference.
If your application relies on location services to function properly, you should include the UIRequiredDeviceCapabilities
key in the application’s Info.plist
file. You use this key to specify the location services that must be present in order for your application to run. The App Store uses the information in this key from preventing users from downloading applications to devices that do not contain the listed features.
The value for the UIRequiredDeviceCapabilities
is an array of strings indicating the features that your application requires. There are two strings relevant to location services:
Include the location-services
string if you require location services in general.
Include the gps
string if your application requires the accuracy offered only by GPS hardware.
Important: If your application uses location services but is able to operate successfully without them, do not include the corresponding strings in the UIRequiredDeviceCapabilities
key.
For more information about the UIRequiredDeviceCapabilities
key, see Information Property List Key Reference.
The Core Location framework lets you locate the current position of the device and use that information in your application. The framework uses information obtained from the built-in cellular, Wi-Fi, or GPS hardware to triangulate a location fix for the device. It reports that location to your code and, depending on how you configure the service, also provides periodic updates as it receives new or improved data.
There are two different services you can use to get the user’s current location:
The standard location service is a configurable, general-purpose solution and is supported in all versions of iOS.
The significant-change location service offers a low-power location service for devices with cellular radios. This service is available only in iOS 4.0 and later and can also wake up an application that is suspended or not running.
Gathering location data is a power-intensive operation. It involves powering up the onboard radios and querying the available cell towers, Wi-Fi hotspots, or GPS satellites, which can take several seconds. Leaving the standard location service running for extended periods can drain the device’s battery. (The significant-change location service drastically reduces battery drain by monitoring only cell tower changes, but the service works only on devices with cellular radios.) For most applications, it is usually sufficient to establish an initial position fix and then acquire updates only periodically after that. If you are sure you need regular position updates, you should use the significant-change location service where you can; otherwise, you should configure the parameters of the standard location service in a way that minimizes its impact on battery life.
Every iOS-based device is capable of supporting location services in some form but there are still situations where location services may not be available:
The user can disable location services in the Settings application.
The user can deny location services for a specific application.
The device might be in Airplane mode and unable to power up the necessary hardware.
For these reasons, it is recommended that you always call the locationServicesEnabled
class method of CLLocationManager
before attempting to start either the standard or significant-change location services. (In iOS 3.x and earlier, check the value of the locationServicesEnabled
property instead.) If this class method returns YES
, you can start location services as planned. If it returns NO
and you attempt to start location services anyway, the system prompts the user to confirm whether location services should be reenabled. Given that location services are very likely to be disabled on purpose, the user might not welcome this prompt.
The standard location service is the most common way to get the user’s current location because it is available on all devices and in all versions of iOS. Before using this service, you configure it by specifying the desired accuracy of the location data and the distance that must be traveled before reporting a new location. When you start the service, it uses the specified parameters to determine which radios to enable and then proceeds to report location events to your application.
To use the standard location service, create an instance of the CLLocationManager
class and configure its desiredAccuracy
and distanceFilter
properties. To begin receiving location notifications, assign a delegate to the object and call the startUpdatingLocation
method. As location data becomes available, the location manager notifies its assigned delegate object. If a location update has already been delivered, you can also get the most recent location data directly from the CLLocationManager
object without waiting for a new event to be delivered.
Listing 1-1 shows a sample method that configures a location manager for use. This method is part of a class that caches its location manager object in a member variable for later use. (The class also conforms to the CLLocationManagerDelegate
protocol and so acts as the delegate for the location manager.) Because the application does not need precise location data, it configures the location service to report the general area of the user and notify it only when the user moves a significant distance, which in this case is half a kilometer.
Listing 1-1 Starting the standard location service
- (void)startStandardUpdates |
{ |
// Create the location manager if this object does not |
// already have one. |
if (nil == locationManager) |
locationManager = [[CLLocationManager alloc] init]; |
locationManager.delegate = self; |
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer; |
// Set a movement threshold for new events. |
locationManager.distanceFilter = 500; |
[locationManager startUpdatingLocation]; |
} |
The code for receiving location updates from this service is shown in “Receiving Location Data from a Service.”
In iOS 4.0 and later, you can use the significant-change location service to receive location events. This service offers a significant power savings and provides accuracy that is good enough for most applications. It uses the device’s cellular radio to determine the user’s location and report changes in that location, allowing the system to manage power usage much more aggressively than it could otherwise. This service is also capable of waking up an application that is currently suspended or not running in order to deliver new location data.
To use the significant-change location service, create an instance of the CLLocationManager
class, assign a delegate to it, and call the startMonitoringSignificantLocationChanges
method as shown in Listing 1-2. As location data becomes available, the location manager notifies its assigned delegate object. If a location update has already been delivered, you can also get the most recent location data directly from the CLLocationManager
object without waiting for a new event to be delivered.
Listing 1-2 Starting the significant-change location service
- (void)startSignificantChangeUpdates |
{ |
// Create the location manager if this object does not |
// already have one. |
if (nil == locationManager) |
locationManager = [[CLLocationManager alloc] init]; |
locationManager.delegate = self; |
[locationManager startMonitoringSignificantLocationChanges]; |
} |
As with the standard location service, location data is delivered to the delegate object as described in “Receiving Location Data from a Service.”
If you leave this service running and your application is subsequently suspended or terminated, the service automatically wakes up your application when new location data arrives. At wake-up time, your application is put into the background and given a small amount of time to process the location data. Because your application is in the background, it should do minimal work and avoid any tasks (such as querying the network) that might prevent it from returning before the allocated time expires. If it does not, your application may be terminated.
Whether you use the standard location service or the significant-change location service to get location events, the way you receive those events is the same. Whenever a new event is available, the location manager reports it to the locationManager:didUpdateToLocation:fromLocation:
method of its delegate. If there is an error retrieving an event, the location manager calls the locationManager:didFailWithError:
method of its delegate instead.
Listing 1-3 shows the delegate method for receiving location events. Because the location manager object sometimes returns cached events, it is recommended that you check the timestamp of any location events you receive. (It can take several seconds to obtain a rough location fix, so the old data simply serves as a way to reflect the last known location.) In this example, the method throws away any events that are more than fifteen seconds old under the assumption that fairly recent events are likely to be good enough. If you were implementing a navigation application, you might want to lower the threshold.
Listing 1-3 Processing an incoming location event
// Delegate method from the CLLocationManagerDelegate protocol. |
- (void)locationManager:(CLLocationManager *)manager |
didUpdateToLocation:(CLLocation *)newLocation |
fromLocation:(CLLocation *)oldLocation |
{ |
// If it's a relatively recent event, turn off updates to save power |
NSDate* eventDate = newLocation.timestamp; |
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow]; |
if (abs(howRecent) < 15.0) |
{ |
NSLog(@"latitude %+.6f, longitude %+.6f\n", |
newLocation.coordinate.latitude, |
newLocation.coordinate.longitude); |
} |
// else skip the event and process the next one. |
} |
In addition to a location object’s timestamp, you can also use the accuracy reported by that object as a means of determining whether you want to accept an event. As it receives more accurate data, the location service may return additional events, with the accuracy values reflecting the improvements accordingly. Throwing away less accurate events means your application wastes less time on events that cannot be used effectively anyway.
In iOS 4.0 and later, applications can use region monitoring to be notified when the user crosses geographic boundaries. You can use this capability to generate alerts when the user gets close to a specific location. For example, upon approaching a specific dry cleaners, an application could notify the user to pick up any clothes that had been dropped off and are now ready.
Regions associated with your application are tracked at all times, including when your application is not running. If a region boundary is crossed while an application is not running, that application is relaunched into the background to handle the event. Similarly, if the application is suspended when the event occurs, it is woken up and given a short amount of time to handle the event.
Before attempting to monitor any regions, your application should check to see if region monitoring is supported on the current device. There are several reasons why region monitoring might not be available:
The device may not have the hardware needed to support region monitoring.
The user may have disabled location services in the Settings application.
The device might be in Airplane mode and unable to power up the necessary hardware.
For these reasons, it is recommended that you always call the regionMonitoringAvailable
and regionMonitoringEnabled
class methods of CLLocationManager
before attempting to monitor regions. The regionMonitoringAvailable
method lets you know whether the underlying hardware supports region monitoring. If it returns NO
, there is no chance that your application will ever be able to use region monitoring on the device. If region monitoring is available, the regionMonitoringEnabled
method reports whether the feature is currently enabled. If region monitoring is available but not enabled when you attempt to monitor a region, the system prompts the user to confirm whether region monitoring should be reenabled. Given that the feature is likely to be disabled on purpose, the user might not welcome this prompt.
To begin monitoring a region, you must define the region and register it with the system. Regions are defined using the CLRegion
class, which currently supports the creation of circular regions. Each region you create must include both the data that defines the desired geographic area and a unique identifier string. (The identifier string is required and is the only guaranteed way for your application to identify regions later.) To register a region, you call the startMonitoringForRegion:desiredAccuracy:
method of your CLLocationManager
object.
Listing 1-4 shows a sample method that creates a new region based on a circular Map Kit overlay. The overlay’s center point and radius form the boundary for the region, although if the radius is too large to be monitored, it is reduced automatically. After registering the region, the region object itself can be released. Core Location stores the data associated with a region but does not typically store the region object itself.
Listing 1-4 Creating and registering a region based on a Map Kit overlay
- (BOOL)registerRegionWithCircularOverlay:(MyCircle*)overlay andIdentifier:(NSString*)identifier |
{ |
// Do not create regions if support is unavailable or disabled. |
if ( ![CLLocationManager regionMonitoringAvailable] || |
![CLLocationManager regionMonitoringEnabled] ) |
return NO; |
// If the radius is too large, registration fails automatically, |
// so clamp the radius to the max value. |
CLLocationDegrees radius = overlay.radius; |
if (radius > self.locManager.maximumRegionMonitoringDistance) |
radius = self.locManager.maximumRegionMonitoringDistance; |
// Create the region and start monitoring it. |
CLRegion* region = [[CLRegion alloc] initCircularRegionWithCenter:overlay.coordinate |
radius:radius identifier:identifier]; |
[self.locManager startMonitoringForRegion:region |
desiredAccuracy:kCLLocationAccuracyHundredMeters]; |
[region release]; |
return YES; |
} |
Monitoring of a region begins immediately after registration. However, do not expect to receive an event right away. Only boundary crossings can generate an event. Thus, if at registration time the user’s location is already inside the region, the location manager does not generate an event. Instead, you must wait for the user to cross the region boundary before an event is generated and sent to the delegate.
You should always be judicious when specifying the set of regions to monitor. Regions are a shared system resource and the total number of regions available systemwide is limited. For this reason, Core Location limits the number of regions that may be simultaneously monitored by a single application. To work around these limits, you should consider registering only those regions in the user’s immediate vicinity. As the user’s location changes, you can remove regions that are now farther way and add regions coming up on the user’s path. If you attempt to register a region and space is unavailable, the location manager calls the locationManager:monitoringDidFailForRegion:withError:
method of its delegate with the kCLErrorRegionMonitoringFailure
error code.
Every time the user’s current location crosses a boundary region, the system generates an appropriate region event for your application. If your application is already running, these events go directly to the delegates of any current location manager objects. If your application is not running, the system launches it in the background so that it can respond. Applications can implement the following methods to handle boundary crossings:
The system does not report boundary crossings until the boundary plus a designated cushion distance is exceeded. You specify the desired cushion distance for a region when you register it using the startMonitoringForRegion:desiredAccuracy:
method. This cushion value prevents the system from generating numerous entered and exited events in quick succession while the user is traveling close the edge of the boundary.
When a region boundary is crossed, the most likely response is to alert the user of the proximity to the target item. If your application is running in the background, you can use local notifications to alert the user; otherwise, you can simply post an alert.
If your application needs location updates delivered whether the application is in the foreground or background, there are multiple options for doing so. The preferred option is to use the significant location change service to wake your application at appropriate times to handle new events. However, if your application needs to use the standard location service, you can declare your application as needing background location services.
An application should request background location services only if the absence of those services would impair its ability to operate. In addition, any application that requests background location services should use those services to provide a tangible benefit to the user. For example, a turn-by-turn navigation application would be a likely candidate for background location services because of its need to track the user’s position and report when it is time to make the next turn.
The process for configuring a background location application is described in “Executing Code in the Background” in iOS Application Programming Guide.
Receiving and transmitting data using the radios of an iOS-based device require more power than any other operation on the device. Because Core Location relies on these radios to determine the user’s location, you should use location services judiciously in your applications. Most applications do not need location services to be running all the time, and so turning off those services is the simplest way to save power.
Turn off location services when you are not using them. This may seem obvious but it is worth repeating. With the exception of navigation applications that offer turn-by-turn directions, most applications do not need location services to be on all the time. Turn location services on just long enough to get a location fix and then turn them off. Unless the user is in a moving vehicle, the current location should not change frequently enough to be an issue. And you can always start location services again later if needed.
Use the significant-change location service instead of the standard location service whenever possible. The significant-change location service provides significant power savings while still allowing you to leave location services running. This is highly recommended for applications that need to track changes in the user’s location but do not need the higher precision offered by the standard location services.
Use lower-resolution values for the desired accuracy unless doing so would impair your application. Requesting a higher accuracy than you need causes Core Location to power up additional hardware and waste power for precision you are not using. Unless your application really needs to know the user’s position within a few meters, do not put the values kCLLocationAccuracyBest
or kCLLocationAccuracyNearestTenMeters
in the desiredAccuracy
property. And remember that specifying a value of kCLLocationAccuracyThreeKilometers
does not prevent the location service from returning better data. Most of the time, Core Location can return location data with an accuracy within a hundred meters or so using Wi-FI and cellular signals.
Turn off location events if the accuracy does not improve over a period of time. If your application is not receiving events with the desired level of accuracy, you should look at the accuracy of events you do receive and see if it is improving or staying about the same over time. If accuracy is not improving, it could be because the desired accuracy is simply not available at the moment. Turning off location services and trying again later prevents your application from wasting power.
Last updated: 2010-05-20