Core Location Tutorial for iOS: Tracking Visited Locations
In this Core Location tutorial, you will learn how to use visit monitoring to track a user’s visited locations. By Andrew Kharchyshyn.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Core Location Tutorial for iOS: Tracking Visited Locations
25 mins
- Introduction
- Getting Started
- AppDelegate.swift
- PlacesTableViewController.swift
- MapViewController.swift
- Location.swift
- LocationsStorage.swift
- Core Location: Asking for User Locations
- Providing a Proper Description
- Asking for Locations Permissions
- Asking for Notifications Permissions
- Choosing the Most Appropriate Locations Data
- Visit Monitoring
- Subscribe to Location Changes
- CLLocationManager
- Location Description
- Sending Local Notifications
- Faking Data (Optional)
- Faking CLVisits
- Set Up locationManager
- Handle Fake Visits
- Persisting Location Data
- Saving Records on Disk
- Saving a Current Location
- Setting up the App to Use Stored Data
- Setting up a Table View
- Updating the List When a Location is Logged
- Setting up MapView With All Logged Locations
- Where to Go From Here?
Choosing the Most Appropriate Locations Data
The Core Location framework has many ways to track a user’s location and each has different characteristics:
- Standard location services: High battery impact. High location precision. Great for navigational or fitness apps.
- Significant location changes: Medium battery impact. Medium location precision. Low stops precision.
- Regional monitoring: Low battery impact. Great location precision. Requires specific regions in order to monitor.
None of these is fully suitable for your app. Low battery impact is a must — a user is unlikely to use the app otherwise. What’s more, regional monitoring is also undesirable because you limit user movement to some specific regions.
Fortunately, there is one other API you can use.
Visit Monitoring
Visit monitoring allows you to track destinations — places where the user stops for a while. It wakes the app whenever a new visit is detected and is very energy efficient and not tied to any landmark.
Subscribe to Location Changes
Now that you know which of the many Core Location APIs you’ll use to get the user’s location, it’s time to start implementing it!
CLLocationManager
In the AppDelegate.swift, below this line:
locationManager.requestAlwaysAuthorization()
add the following code:
locationManager.startMonitoringVisits()
locationManager.delegate = self
The first line initiates the listening feature. Core Location uses delegate callbacks to inform you of location changes.
Now, add this extension at the bottom of the file:
extension AppDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
// create CLLocation from the coordinates of CLVisit
let clLocation = CLLocation(latitude: visit.coordinate.latitude, longitude: visit.coordinate.longitude)
// Get location description
}
func newVisitReceived(_ visit: CLVisit, description: String) {
let location = Location(visit: visit, descriptionString: description)
// Save location to disk
}
}
The first method is the callback from CLLocationManager
when the new visit is recorded and it provides you with a CLVisit
.
CLVisit
has four properties:
-
arrivalDate
: The date when the visit began. -
departureDate
: The date when the visit ended. -
coordinate
: The center of the region that the device is visiting. -
horizontalAccuracy
: An estimate of the radius (in meters) of the region.
You need to create a Location
object from this data and, if you recall, there is an initializer that takes the CLVisit
, date and description string:
init(_ location: CLLocationCoordinate2D, date: Date, descriptionString: String)
The only thing missing in the above is descriptionString
.
Location Description
To get the description, you will use CLGeocoder
. Geocoding is the process of converting coordinates into addresses or place names in the real world. If you want to get an address from a set of coordinates, you use reverse geocoding. Thankfully, Core Location gives us a CLGeocoder
class which does this for us!
Still in AppDelegate.swift, add this property at the top of the class:
static let geoCoder = CLGeocoder()
Now, at the bottom of locationManager(_:didVisit:)
, add the following code:
AppDelegate.geoCoder.reverseGeocodeLocation(clLocation) { placemarks, _ in
if let place = placemarks?.first {
let description = "\(place)"
self.newVisitReceived(visit, description: description)
}
}
Here, you ask geoCoder
to get placemarks from the location. The placemarks contain a bunch of useful information about the coordinates, including their addresses. You then create a description string out of the first placemark. Once you have the description string, you call newVisitReceived(_:description:)
.
Sending Local Notifications
Now, it’s time to notify a user when the new visit location is logged. At the bottom of newVisitReceived(_:description:)
, add the following:
// 1
let content = UNMutableNotificationContent()
content.title = "New Journal entry 📌"
content.body = location.description
content.sound = .default
// 2
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: location.dateString, content: content, trigger: trigger)
// 3
center.add(request, withCompletionHandler: nil)
With the above, you:
- Create notification content.
- Create a one second long trigger and notification request with that trigger.
- Schedule the notification by adding the request to notification center.
Build and run the app. At this point, the app is usable in that it logs visits and notifies the user.
If you are using a real device and have some time for a walk, you can test your work right now. Go some place and stop to have a coffee. Visits require you remain at a place for some period of time. You should receive some notifications, like this:
The visits are being recorded, but the visits are not yet persisted.
Faking Data (Optional)
Walking is good for your body, but it might be a problem to do it right now in the middle of building this app! To test the app without actually walking, you can use the Route.gpx file. This kind of file allows you to simulate the device or simulator GPS location. This particular file will simulate a walk around Apple’s campus in Cupertino.
To use it, in the Debug area, click the “Simulate Location” icon, and then select “Route” from the list:
You can open the tab with a map or Maps app to see the walking route.
Faking CLVisits
iOS records CLVisit
s behind the scenes, and sometimes you might wait for up to 30 minutes in order to get the callback! To avoid this, you’ll need to implement mechanics that fake CLVisit
recording. You’ll create CLVisit
instances, and since CLVisit
has no accessible initializer, you’ll need to make a subclass.
Add this to the end of AppDelegate.swift:
final class FakeVisit: CLVisit {
private let myCoordinates: CLLocationCoordinate2D
private let myArrivalDate: Date
private let myDepartureDate: Date
override var coordinate: CLLocationCoordinate2D {
return myCoordinates
}
override var arrivalDate: Date {
return myArrivalDate
}
override var departureDate: Date {
return myDepartureDate
}
init(coordinates: CLLocationCoordinate2D, arrivalDate: Date, departureDate: Date) {
myCoordinates = coordinates
myArrivalDate = arrivalDate
myDepartureDate = departureDate
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
With this subclass, you can provide initial values for CLVisit
‘s properties.
Set Up locationManager
Now you need the locationManager
to notify you when the location changes. For this, at the end of application(_:didFinishLaunchingWithOptions:)
, before return statement, add the following:
// 1
locationManager.distanceFilter = 35
// 2
locationManager.allowsBackgroundLocationUpdates = true
// 3
locationManager.startUpdatingLocation()
Here’s what these lines do:
- Receive location updates when location changes for n meters and more.
- Allow location tracking in background.
- Start listening.
You can comment out these 3 lines to turn off the visits faking.