By pulling a user’s location, you make the data in your app more relevant to your users. Shawn Welch discusses iOS 5 enhancements to location services including forward and reverse geocoding, placemarks, and regions.

This chapter is from the book

One of the obvious benefits of iOS is that it’s a mobile platform. iOS devices move throughout the world; calling upon a device’s location while utilizing Core Location and Map Kit helps you provide a better context for the data in your app. For example, if a user is in San Francisco and does a search for “Bart,” they’re probably looking for information on the Bay Area Rapid Transit system (aka the BART), whereas in other parts of the world that same search may be looking for a pop culture reference. By pulling a user’s location, you make the data in your app more relevant to your users. New to iOS 5 are enhancements to location services including forward and reverse geocoding, placemarks, and regions.

Getting Started with Core Location and Map Kit

Core Location is a set of Objective-C classes built into the Core Services layer of iOS. Core Location was designed to simplify the process of working with location by providing a set of APIs that facilitate location monitoring and various location-data conversions (such as latitude/longitude coordinates to human readable addresses and vice versa). The Core Location framework is data oriented and can be used to obtain relevant location information as needed for check-in services, user tracking, nearby searches, and more.

The Core Location Manager (CLLocationManager) manages this flow of data, and controls your app’s interaction with the physical hardware used to determine location. The location manager passes new location data that is retrieved from hardware to its delegate and then encapsulates it in a CLLocation object. This object contains a determination of the latitude and longitude coordinates of the device as well as information about the accuracy of the determination. The location manager also calculates speed and heading (based on the observed change in location), and likewise encapsulates it in the CLLocation object.

Unlike the Core Location framework, the Map Kit framework is visually oriented—it communicates location data back to a user through maps. Using Map Kit, you can seamlessly embed various map views into your app using Google Maps data as a service provider. Map Kit also has some very handy (and entirely automated) APIs designed for visually based real-time user tracking on a map.

When used in combination, Core Location and Map Kit allow you to create feature-rich, location-aware apps for all iOS devices.

NOTE

As mentioned by the Apple Terms of Service agreement and the Apple iOS Developer agreement, because the Map Kit framework uses Google Services, the use of this framework and data holds you liable to the Google Maps/Google Earth API terms of service. For more information about these terms of service, visit http://code.google.com/apis/maps/iphone/terms.html. While this will not be an issue for most apps, it’s something you should be aware of when making your apps.

Linking Core Location and Map Kit frameworks

Before you can make your app location aware, you must first link the Core Location framework to your project. If you plan to use map services (for example, to show locations on a map), you should also link the Map Kit framework. These frameworks are represented by the libraries CoreLocation.framework and MapKit.framework.

To add the Core Location and Map Kit frameworks to your project, refer to the procedures in Chapter 1, To Link New Frameworks in an Xcode Project, and add CoreLocation.framework and MapKit.framework (Figure 4.1). Next, import the following code in the appropriate header (.h) files:

How Location Is Determined

When an iOS device attempts to pinpoint its location, it relies on three data sources to make the determination. Each of these data sources provides a different level of speed (the time it takes to make the determination), performance (the amount of power used to make the determination), and accuracy (the +/− distance in meters). Table 4.1 (on the next page) highlights the three technologies used and ranks them based on their relative properties.

Fairly high compared to other methods, especially during continuous tracking.

As you can see, there are varying levels of performance, speed, and accuracy between each location source. Determining location through cell phone towers is very fast and very efficient but it’s not the most accurate determination. This is not always an issue. For example, if you’re making a taxi app and you want to list the phone numbers for all of the taxi services in the area, you probably only need to know what city someone is in. Since taxi services will drive a ways to pick you up, it’s not necessarily relevant that a person is standing on the corner of Arlington and Boylston.

At the other end of the spectrum, a fitness app that tracks your running progress or a turn-by-turn GPS app would require more accurate location data. Your users would be annoyed if their turn-by-turn app missed a turn by about 100 meters. In between these extremes, where accuracy is important but not as much as turn-by-turn, would be a location check-in service. In this case it’s not critical to your app’s function that a person be in the exact location their device claims to be, so you can trade off the accuracy for better battery performance.

Another important take-away from Table 4.1 is that not every location data source is available on every iOS device. Only devices configured with a cellular radio (iPhones and 3G-enabled iPads) are able to determine location through cell towers. Additionally, GPS is only available on the iPhone models 3G and later and all 3G-enabled iPads. If accurate location data is critical to the operation of your app (such as for turn-by-turn navigation or Find My Friends), then you should configure your app’s info property list to require the appropriate hardware.

You can add two levels of restrictions for location-based hardware capabilities. When added to the UIRequiredDeviceCapabilities array in your info property list, these keys provide the following restrictions (Figure 4.2):

location-services: Requires that device has some form of location service available. Used as a general restriction.

gps: Requires device with GPS hardware.

Remember, add these keys to your app only if your app is unable to operate without them. If location is simply a nice feature that improves user experience, then your app should not require specific hardware. For example, a movie theatre app might work best when the app can automatically determine your location using hardware. But this app would also work if a user simply types in their ZIP code for nearby theaters. In this case, the app should not include location-services as required hardware.

NOTE

The required hardware capability “armv7” in Figure 4.2 simply indicates that the app must run on an iOS device and will be in your required capabilities list by default when a new iOS app is created in Xcode.

Fortunately, while it is important for you to be aware of the various source hardware capabilities, it is not necessary for you to specify which piece of hardware your application should use—iOS chooses the hardware automatically. When working with the Core Location Manager to manage updates from hardware, you simply specify your desired accuracy. The desired accuracy of the location manager is measured in meters and can be set using a CLLocationAccuracy constant. These constants are defined by the iOS SDK and indicate by name their intended use (Table 4.2).

Table 4.2. Core Location Accuracy Constants

Constant

Intended Use

kCLLocationAccuracyBest

The default value for the location manager desired accuracy. In this condition, iOS does its best to provide the best location possible with locationbased hardware.

kCLLocationAccuracyBestForNavigation

This condition is the most accurate of all the available configurations and should only be used in apps where absolute precision is necessary (turn-by-turn). ioS actually achieves better than "best" in this condition by using additional sensors beyond location-based hardware to provide highlyaccurate data at all times. this condition is fairly power intensive and is designed to operate while the device is plugged in to a power source.

kCLLocationAccuracyNearesttenMeters

Set the desired accuracy to 10 meters. This condition works well for checkin type applications.

kCLLocationAccuracyHundredMeters

Set the desired accuracy to 100 meters. this condition works well for nearby services that operate under the assumption your user is walking (such as nearby restaurants or friends close by).

kCLLocationAccuracyKilometer

Set the desired accuracy for 1 kilometer. This condition works well for city-based searches such as the nearest movie theater.

kCLLocationAccuracythreeKilometers

Set the desired accuracy for 3 kilometers. This condition works well for city-based searches where you're looking for services available in that city and are not necessarily sorting by the closest service.

NOTE

While the accuracy can be defined, it is not a guarantee. iOS will do its best to optimize accuracy based on the conditions in the table and will automatically switch between available sources to reach the desired accuracy level.

Location Permissions

I don’t know about you, but I can’t count how many times I’ve launched an app and was surprised to be asked for access to my location. Nine times out of ten, if I wasn’t expecting to provide an app with my location, I won’t allow it.

The moral of this story is when you use location in apps, you have to ask for permission first—there’s no way around it. The harsh truth about location-aware apps is that many users don’t like providing apps with their location data. Not everyone will enable location for your app, even if it makes your app super awesome. So you need to be prepared to handle conditions where your app does not have permission to use the services you planned on using.

Controlling Location Permissions

The first time your app attempts to determine a device’s location, iOS will prompt a permission dialog to the user that indicates your action. This action occurs whether you’re using a CLLocationManager (Core Location) or an MKMapView (Map Kit) configured to show the device’s location. By default, this dialog will simply say, “Your App Would Like to Use Your Current Location,” with the choices of Don’t Allow and OK. When you’re determining location using the CLLocationManager, you have the option of setting a purpose string, which is your opportunity to explain in the permission dialog why your app needs access to a user’s location. (Figure 4.3).

We’ll get into the specifics of the CLLocationManager in the next section; however, while we’re on the subject of permissions you can configure the custom purpose message of a CLLocationManager by setting its managed property purpose (Figure 4.3).

1 [locationManagersetPurpose:@"My Custom Purpose Message..."];

Determining Location Service Availability

Before you attempt to use location services in your app, you should first check to see if they’re available. There are many reasons why location services might be unavailable. First and foremost, the necessary hardware might be disabled because a device is in airplane mode or because the user has turned off location services globally for all apps. Second, a user might have disallowed access to your app specifically either in the location services permission dialog mentioned in the previous section or in the Settings app. Finally, the Parental Controls section of the Settings app on every iOS device allows parents the choice to prevent apps from using location data. This condition should be handled separately from the permission dialog because in this case your users will never be presented with a dialog asking for permission.

With these conditions in mind, the CLLocationManager offers two class methods that allow you to determine first, whether or not location services are enabled, and second, the authorization status of your specific app. These class methods are [CLLocationManagerlocationServicesEnabled] and [CLLocationManagerauthorizationStatus], with the conditions and possible values demonstrated in the following code block:

1// Check to see if location services are enabled2if([CLLocationManager locationServicesEnabled]){34NSLog(@"Location Services Enabled");56// Switch through the possible location7// authorization states8switch([CLLocationManager authorizationStatus]){9casekCLAuthorizationStatusAuthorized:10NSLog(@"We have access to location services");11break;12casekCLAuthorizationStatusDenied:13NSLog(@"Location services denied by user");14break;15casekCLAuthorizationStatusRestricted:16NSLog(@"Parental controls restrict location services");17break;18casekCLAuthorizationStatusNotDetermined:19NSLog(@"Unable to determine, possibly not available");20 }21 }22else{23// locationServicesEnabled was set to NO24NSLog(@"Location Services Are Disabled");25 }

This code block is fairly straightforward. Functionally, we’re not doing much more than printing log messages based on the possible location services enabled and location authorization states. In line 2 we first check to see if location services are enabled. If this condition results to NO, we jump down to line 22 and handle our disabled condition. This condition would result as NO if the device were in airplane mode or if location services were disabled globally in the Settings app. In lines 8 through 20 we handle the condition that location services are enabled by evaluating a switch statement based on the possible authorization status values. The possible values for the location authorization status are

kCLAuthorizationStatusAuthorized: Your app is able to use location services.

kCLAuthorizationStatusDenied: The user has chosen to deny your app access to location services.

kCLAuthorizationStatusRestricted: You do not have access to location services because availability is restricted by parental controls. This means the user will never be presented a permission dialog.

kCLAuthorizationStatusNotDetermined: Your app was unable to determine if location services are authorized. This authorization state is most likely caused by location services being disabled or some other fringe case caused by errors. In our code block, we would probably never reach this condition because we first check to see if location services are enabled. But, if you were to check this value outside of our code block while services are disabled, the status would be unknown.

NOTE

Even though the unknown authorization status is most likely the cause of services being disabled, you should not use this status value as a condition in your app indicating services are disabled. This status could also be the cause of some unknown error iOS experienced when attempting to check on your app’s status (possibly caused hardware or software issues, uncorrectable by you or the user). To indicate services are disabled, use the locationServicesEnabled Boolean.