Core Location Best Practices

Session 716 WWDC 2016

Discover how to give users a great location-aware experience while conserving power across all Apple platforms. Learn best practices for using Core Location, how an App can use and manage Circular and Beacon Regions, defer or pause location updates, and how to use Visit Monitoring to determine the interesting places the user has been. Get an overview of how a user authorizes an App's use of location services.

[ Music ]

[ Applause ]

Good afternoon.

I'm Brad, and today we are going to talk about Core Location best practices.

Now just so we're all on the same page, we're going to start off by talking about the major features of Core Location.

For veterans of our API, this will be largely review.

But for newcomers, it will be well, you'll want to consult the documentation after this session.

After that, we'll switch over to talking about best practices for using Core Location.

Now iOS is the only platform where all of our APIs are available, so it will be our focus today.

However, if you are interested in macOS, tvOS or watchOS, you might want to stick around, because a lot of the content will be applicable to those platforms, and we'll talk about them explicitly at the end.

With that, let's get started talking about Core Locations, major features.

The first one is the authorization API.

Now, you should know that the authorization API is required in order to access the user's location.

You've probably experienced this as a user, if you've ever seen a prompt like this one.

Here, Core Location is asking the user if they'd like to authorize the camera app to access their location.

We offer two different versions of authorization that your app can request.

The first one is when-in-use authorization, and just like it sounds like, when your app receives when-in-use authorization, it is allowed to access the user's location whenever it is in use.

We'll define what it means to be in use in just a moment.

If you'd like to request when in use authorization, you simply call the requestWhenInUseAuthorization method.

We also have AlwaysAuthorization.

When your app has AlwaysAuthorization, it is permitted to access the user's location whenever your app is running.

To request it, you simply use the requestAlwaysAuthorization method.

Whichever authorization level you choose to request, you must provide a usage description in your app's information property list.

Core Location will pull this string out of your info plist, and display it to the user as part of the authorization prompt.

So when is your app considered in use?

Well, if your app is in the foreground, then we'll consider it in use.

This is Potluck, our sample app.

If your app is in the background, but has a blue bar, then it will also be considered in use.

If you're wondering about the blue bar, we'll talk about that in just a few moments.

Finally, if your app is handling WatchConnectivity messages, from a foreground watchOS app, then it will also be considered in use.

Once your app has received authorization from the user, it can use our location sensitive APIs.

The first one we are going to talk about is the bread and butter of Core Location, the standard location service.

It comes in two different versions.

The first version is the single location API.

When you call Request Location, Core Location will do its best to produce an estimate of the user's position, and then deliver that to your delegate.

We also offer the continuous location API.

When you call Start Updating Location, Core Location will produce a stream of location updates, and deliver them all to your delegate.

Remember to stop your location updates when you're done, since otherwise Core Location will continue to compute locations.

We have a few knobs and dials you can turn to adjust how the standard location service works.

For example, Deferred Updates.

When you enable deferred location updates, you're telling Core Location that it's acceptable for us to deliver location updates to your app in large batches.

Sometimes we do this for power reasons.

We also offer automatic pausing.

This is enabled by default.

What it does, is it attempts to detect when your location session has outlived its usefulness to some extent.

Let's consider an example.

Suppose the user is using a run tracking app, and they go for a run, but when they get home, they're tired, and they just want to take a shower, and they forget all about stopping their location session.

Now, unless the app was specifically trying to detect this sort of situation, it's likely that the user's phone would continue to compute locations until it runs out of power.

With automatic pausing, Core Location will attempt to detect situations like this, and automatically stop the location updates.

Core Location also has special support for using the standard location service while in the background.

Once you engage one of these special sessions, Core Location will keep your app running and continue to deliver location updates to it.

If your app has when-in-use authorization, we'll automatically display a blue bar at the top of the screen, thus marking your app as in use, and allowing it to continue to receive location updates.

An always authorized app will still get this background running behavior, but it won't get a blue bar.

It's important to remember to stop your location session when you're done, otherwise it will continue potentially indefinitely.

In order to start one of these background sessions, there are three things your app must do.

The first one is you must enable background location in your information property list.

Now, the easiest way to do that is to navigate to the capabilities tab in Xcode, scroll down to the background mode section, and check the location updates box.

Second, you must set your allowed background location updates property to true.

This indicates that that particular location manager would like to be able to start a background location session.

Finally, you must start your location updates while in the foreground.

If you don't start your updates in the foreground, you won't get this special behavior.

So what happens if you do start your updates in the background?

Well, first off, your app will probably need AlwaysAuthorization since your app won't be considered in use at that time.

Furthermore, Core Location won't take any action to ensure your app continues to run.

So if you have background runtime for some reason, and you decide to start a location session, you might get some updates, but you might also get suspended before you receive all the information you had hoped to receive.

After the standard location service, we have our background monitoring APIs.

The first one we are going to talk about is region monitoring.

Region monitoring allows your app to specify a location that is interesting to it, and Core Location will attempt to determine whenever the user has arrived at or departed from that location.

This will continue even if your app is suspended or in the background, and Core Location will launch your app into the background if necessary to deliver information about the event.

We have two different versions of the region monitoring API.

First, we have circular region monitoring.

When you start circular region monitoring, you describe a circular geographic region that is interesting to your app, and Core Location will attempt to detect entries or exits from that circular region.

We also have Beacon region monitoring.

Beacon region monitoring attempts to detect proximity to iBeacon devices that match a specification provided by your app.

In either case, region monitoring consumes a limited system resource, and so Core Location only allows your app to install a limited number of regions.

However, keep in mind, if you're writing an app that uses Beacon region monitoring, a single beacon region can monitor from many iBeacon devices.

When you're ready to start region monitoring, you simply construct a CL region, either a CL beacon region or a CL circular region, and then pass it to the startMonitoring(for:) method on CL location manager.

When you're done, pass the same region to stopMonitoring(for:).

If you want to use region monitoring to trigger a notification, you might be interested to know that the user notification framework has special support for this through their UNLocationNotificationTrigger class.

The user notification framework is new in iOS 10, but this functionality was previously available through UI local notification.

If you'd like to learn more about the new user notification framework, you might want to view the Introduction to Notifications session online.

Similarly, the HomeKit framework has special support for triggering a HomeKit scene in response to a region monitoring event.

The HM LocationEvent allows you to do this.

If you'd like to learn more about using HM location event, I would encourage you to view HomeKit session from last year.

You can find it online.

In either case, you only need when-in-use authorization to use the user notification framework, or the HomeKit framework special support for region monitoring.

General purpose region monitoring through Core Location requires AlwaysAuthorization.

Similar to Beacon region monitoring, we have Beacon ranging.

Now, every iBeacon device broadcasts three components of information: A UUID, a major ID, and a minor ID.

When a beacon region event fires, you usually don't receive all three components of information.

Using Beacon ranging, you can fill in the missing details.

Ranging is also useful for determining an estimated range from the user's device to the iBeacon device.

Now, range estimates are most accurate when your app is in the foreground, but you can use Beacon ranging in the background.

If you do, however, keep in mind that Core Location won't prevent your app from being suspended, and so you might be suspended before you receive all the information you had hoped to receive.

As an aside, if you're interested in using iBeacon devices, I would highly encourage you to visit developer.apple.com/ibeacon.

Once you agree to the iBeacon license, you'll be able to download the official iBeacon specification for what every well-behaved iBeacon device must do.

The next background monitoring API we are going to talk about is the significant location change monitoring API.

As the name would imply, this monitor's for large changes in the user's location on the order of about a kilometer.

Since we introduced this, quite a bit has changed in Core Location, and at this point, we believe that it's fairly unique.

A lot of apps that use significant location change monitoring would be better served by visit monitoring.

On the topic of visit monitoring, visit monitoring deploys sophisticated algorithms to monitor for places that the user might consider a noteworthy part of their day.

That's why we think many apps would be better served by visit monitoring.

After all, you're usually interested in where the user stops and spends time, rather than where they happen to be when Core Location has detected a large change.

We've gone to great lengths to ensure that visit monitoring has a fairly low power cost, and so you should feel comfortable using it in an all-day scenario.

Like the rest of our background monitoring APIs, visit monitoring will continue even when your app is suspended, and will launch your app in the background to deliver event information.

If you'd like to start visit monitoring, you simply call the startMonitoringVisits method.

When you're done, call stopMonitoringVisits.

The final API we are going to talk about today is the geocoding API.

Core Location supports both forward geocoding, that is converting an address into a coordinate, and reverse geocoding, which is converting coordinates into an address.

Unlike the rest of the APIs we have talked about today, geocoding does not require user authorization, however, if your app is authorized to access the user's location, will automatically use that information when performing forward geocoding.

That is, converting an address into coordinates.

The geocoding API is rate limited, and so you should try not to send too many requests to it.

A great way to avoid hitting the rate limit is the cache results provided by the geocoder.

That way, you don't have to try to geocode the same information multiple times.

We also recommend that you only geocode in response to a user action.

For example, if the user drops a pin on a map, that's an excellent time to geocode that location.

Okay, now that you're familiar with all of our APIs, let's take a look at authorization again.

If your app has when-in-use authorization or always authorization, it can use the standard location service, those special background location sessions, remember, that is starting in the foreground and then entering the background, and Beacon ranging.

If your app has always authorization, it can additionally use our background monitoring APIs.

That is region monitoring, visit monitoring, and significant location change monitoring.

Don't forget that the user notification framework and the HomeKit framework allow you to do special purpose region monitoring with when-in-use authorization.

As far as availability is concerned, all of our APIs are available on iOS.

On macOS, you can use the standard location service, circular region monitoring, significant location change monitoring and geocoding.

On watchOS, we support the standard location service, and geocoding, and finally, on tvOS, you can use the single location API, and the geocoding API.

Alright, let's get started talking about the best practices for using Core Location.

The user's privacy is the most important part about using Core Location.

Location information is highly sensitive.

It describes where we live, where we work, and who we spend our time with.

As such, you must respect the user's privacy when using Core Location.

And that is worth repeating.

You must respect the user's privacy when using Core Location.

Core Location helps facilitate this using our authorization system, but getting the user's permission to access your location, sorry, their location, is only the first step in respecting their privacy.

You should make sure that your app doesn't request any more information than what it absolutely needs in order to satisfy a user request.

Similarly, you should make it clear to the user what you're asking for and how you intend to use it.

There are two concrete things that your app should do if it is going to use Core Location.

First if you're going to talk to a server you should attempt to anonymize those queries as much as possible.

Let's consider an example.

Say you're writing a check-in application.

Well, if the user has tapped the Check-in button, then it makes sense that you should send both user identity information and location to your server all at once, since the user clearly wants your server to know both.

However, if you're writing, say, a weather application, then the user probably doesn't gain much benefit from your server being able to associate both identity and location.

And so the user would be better served by an anonymous query in that instance.

Additionally, if you plan on persisting location information to disc, you should use the file protection APIs.

Now this one is relatively simple, since file protection is enabled by default since iOS 8.

However, it is still possible to create unprotected files, and so you should make sure that if you're going to be persisting location information, you should not do so in an unprotected file.

After privacy, power is the second most important thing to consider when using Core Location.

The power cost of using Core Location is highly variable.

At one extreme, we have Beacon region monitoring, which is, for all intents and purposes, free.

And at the other extreme, we have continuous location sessions, which can drain the user's battery in just a few hours.

No matter how cool your app is, if it drains the user's battery faster than they would expect, they're not going to enjoy using it.

Thus, you must design your app with power in mind.

Sometimes that means sacrificing accuracy or latency in order to achieve the power metrics that your users would expect.

To achieve that, you must use the right API.

But Core Location has a lot of APIs.

How do you know which one is the right one?

I put together a simple decision tree that should help guide you toward the right API.

It's not meant to capture every possibility, but it should be enough to get you started.

The first question to consider is whether you want the user's location now, or sometime in the future.

If you're interested in the future, then you want to use one of our background monitoring APIs.

Either region monitoring, if you're interested in a specific location, or visit monitoring if you're interested in any location that the user stops at.

If you want to know about the present, then you want to use our standard location service.

Now, there's a lot of different ways you can use it.

So let's drill into some specifics.

If you're writing a fitness application, then we recommend you use deferred location updates in a continuous location session.

If you're writing a navigation application, then you probably want to use a continuous location session.

If your app is continuously updating UI, then you also probably want to use a continuous location session.

The final question to consider is whether your app is recording a track of the user's location.

If it is, then we would recommend you use deferred location updates.

In most other situations, we would recommend using the single location API.

You may have noticed the battery icon at the bottom of this slide.

These aren't meant to be taken literally, they're just a guide to give you a rough idea of how much power each service uses.

Okay, for authorization, we highly recommend you use when-in-use authorization whenever possible.

Users like knowing that you can't track them without their knowledge.

Furthermore, you should communicate with your users and make sure they understand why you're asking for the authorization level you are, and how you intend to make use of it.

Remember, Core Location displays a prompt that includes a usage description key provided by your app.

This is an excellent opportunity to communicate with the user, but hopefully it's not the last opportunity you'll take.

Your app should be prepared to direct users to settings.

Core Location only displays a limited number of authorization prompts for your app, and so at some point, if you want your authorization level changed, users will have to go to settings and do it themself.

If you direct your UI application to open the UI application, open settings, URL string constant, then iOS will display your apps settings pane.

For Potluck, our sample app, it looks something like this.

As you can see, the user is just a few taps away from changing Potluck's authorization.

We've seen a lot of developers get confused by Core Location and it's threading requirements.

The first thing to remember is that Core Location requires you to create your CLLocationManager on a thread that has a runloop.

When the location manager is initialized, it will determine which run loop is associated with the current thread.

It will then deliver all delegate callbacks on that runloop.

Now, for many applications, the main thread is the only thread with a runloop.

As such, it is safe to create your location manager on the main thread, but if you do, be careful.

You have to keep your main thread active in order to handle UI events.

If you spend a lot of time processing location information, then your app will become unresponsive.

We recommend that you always interact with location manager, that is, call its methods from the thread that it was created on.

We believe this simplifies interactions with location manager.

Let's look at some code.

Here I am requesting authorization.

Let's suppose I've already added the necessary usage description keys to my information property list.

Here, I'm creating a location manager, and I'm calling request when-in-use authorization.

Unfortunately, this won't work.

See, the requesWhenInUseAuthorization method is asynchronous, and CLLocationManager when it is deallocated will automatically tear down any outstanding authorization prompts, and so the user probably won't see the authorization prompt at all, since the manager will be deallocated at the end of the function.

Let's try something a little different.

If we put it into a static class property, then it will certainly live long enough.

However, this introduces a new bug.

See, Swift will initialize the manager property the first time it's accessed.

And unless we're very careful and ensure we always access it from a thread with a runloop, this could create a location manager on a thread without a runloop.

We believe this pattern is error prone and discourage its use.

One more try.

Here, we are creating our location manager as an instance property on a ViewController.

Now, view controllers are always constructed on the main thread, and in this instance, the location manager will be created along with the ViewController.

So we are guaranteed that the location manger will be constructed on the main thread.

This is the recommended pattern for creating location managers.

You might also consider attaching it to your app delegate instead.

Now since the standard location service can be the most power hungry of all of Core Location's APIs, it's important to use it correctly.

First we recommend using the request location API whenever possible.

Second, you should ensure that your desired accuracy property is set to the largest value your app can tolerate.

Generally, more accurate locations require more power to create.

As an example, if you request, say, 100 meter accuracy, Core Location usually doesn't need to turn on the GPS in order to satisfy your location request.

Keep in mind that Core Location will give you or could give you a location update that is more accurate than what you requested.

So to say you request a 3 kilometer accuracy location.

This will effectively ask Core Location to give you whatever location is most readily available.

We encourage you to keep automatic pausing enabled.

We've tuned the automatic pausing algorithm to be fairly conservative.

We would be surprised if it stopped your location session at a time when the user was still gaining benefit from it.

As long as you've configured it correctly, you should be able to leave it running all the time.

To configure automatic pausing, simply set the activity type property on your location manager to the appropriate value.

If you'd like to learn more, you should consult our documentation.

Similarly, we recommend leaving or enabling deferred location updates.

Deferred location updates allows Core Location to put the device into a low power state and collect location information passively and then process it in large batches.

This can result in a fairly large power savings compared to a normal continuous location session.

If this is all a little confusing, and you're not entirely sure how each of these settings plays out, you might want to consider using the energy log instrument in Xcode.

This will give you a rough idea of how much power your continuous location sessions are drawing.

Finally, we recommend that you set the allowsBackgroundLocationUpdates property to true only when you're sure that you want to begin a background location session.

We've seen some apps set it to true unconditionally and then just stop their location sessions when they enter the background, but this has a negative side effect.

Here, I've modified Potluck, our sample app, to do exactly that.

You'll see that when I start the session and then home out, there is a blue bar at the top of the screen for just a few seconds.

So here I am starting the session.

Then I hit the home button, and there is a blue bar at the top of the screen.

This can be easily avoided by simply managing your allowsBackgroundLocationUpdates property properly.

Now, our monitoring APIs affect your entire process.

As such, we recommend that you always interact with them from a single location manager, shared by your entire process.

You might want to attach it to your app delegate.

Furthermore, our monitoring APIs will continue until you tell it to stop.

This is true, even if you update your app and remove all references to Core Location and stop linking the framework.

So we highly recommend that whenever your app is launched, you take a moment and assess whether you think Core Location should be doing background monitoring for your app.

Unless you're sure that at that time background monitoring should be running, you should tell Core Location to stop monitoring.

This small snippet of code is all it takes to stop all three of our background monitoring APIs.

If you're writing an app for an indoor venue, you should remember that Core Location will automatically enable indoor location technologies whenever your app sorry, whenever the user is inside your venue.

This only happens if your venue has been surveyed.

If you'd like your venue to get surveyed, go to mapsconnect.apple.com to learn more.

Beacon region monitoring is a highly versatile technology that can be used in all sorts of problem domains, but we think it pairs especially well with indoor scenarios.

It complements indoor location very well.

Beacon region monitoring allows your app to detect proximity to landmarks that you've established inside the venue.

Indoor location allows you to display the user's location on a map, and help them navigate from one location to another.

On watchOS, you should remember that Core Location will automatically leverage the iPhone if it's nearby.

Let us handle talking to the iPhone and using it to compute locations.

Furthermore, we've seen a lot of apps request always authorization on watchOS, even though what they are doing could be accomplished with when-in-use authorization.

If you're thinking of using always authorization on watchOS, we would encourage you to take a second look at when-in-use authorization.

For watchOS 3, we are making startUpdatingLocation available on watchOS.

However, most apps would still probably be better served by requestLocation.

There is an exception, though.

In watchOS 3, we've made background on watch possible for fitness purposes.

So if you're looking at creating a workout application, we believe that startUpdatingLocation is the perfect API or you to use.

For macOS, we only support always authorization.

Furthermore, Core Location will automatically display a prompt when you attempt to access location information.

You don't need to call requestAlwaysAuthorization on macOS.

Since Macs don't tend to move around very much, we believe that requestLocation is usually the API you want to use on macOS.

For tvOS, you have got three powerful APIs, Single Location, Geocoding and MapKit.

Using these APIs, you've got everything you need to create a great location aware experience on Apple TV.

But be extra careful with any information vended to your application through Core Location; tvOS devices usually live in a user's home, and the user's home is among the most sensitive information you can get from Core Location.

You should treat that information with the respect it deserves.

That brings us to the end of our session today.

There are four things I want you to remember as you leave.

First, you must respect the user's privacy.

Part of this is communicating with the user and making sure they understand what you're asking for and how you intend to use it.

Second, conserving power is an essential part of using Core Location.

Sometimes you have to sacrifice accuracy and latency in order to create the experience your users would expect, while still having the power metrics your users would expect.

To do that, you have to make sure you use the correct Core Location API for your problem domain.

Similarly, make sure you use when-in-use authorization whenever you can.

If you're having a hard time getting started with Core Location, I recommend you take a look at Potluck, our sample app.

We updated it this year for Swift 3, so it's looking better than ever.

If you'd like to learn more you can view our supplementary material online or check out one of these related sessions.

With that, thank you for your time.

Enjoy the rest of WWDC.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US