Swift - Core Location
May 8, 2023

References

Tracking Location in Background

To make your app keep getting location information in the background, first add location to UIBackgroundModes key in the info.plist file.

1
locationManager.allowsBackgroundLocationUpdates = true

Battery Consumption vs Background mode

pausesLocationUpdatesAutomatically will enable iOS to detect the situation where an app doesn’t need location update.

1
locationManager.pausesLocationUpdatesAutomatically = true

The behavior of auto pausing location update is controlled by the flag named activityType in LocationManager class.

There are four CLActivityType options. For each type, iOS changes the rule to judge whether a user is “not moving, thus no new location is necessary”.

  • AutomotiveNavigation (For navigating car drivers)
  • CLActivityTypeFitness (For users walking, running, or cycling)
  • CLActivityTypeOtherNavigation (For users moving on other types of vehicles such as a boat, a train or an airplane)
  • Other (For unknown type of motion or transportation)
1
locationManager.activityType = .fitness

pausesLocationUpdatesAutomatically being true resumes location tracking only when the app is in the foreground. Therefore, if your app’s aim is to keep logging the user’s location in the background, you should set false to pausesLocationUpdatesAutomatically flag. You don’t have to set activityType if you set pausesLocationUpdatesAutomatically to false.

Location Accuracy and Battery Consumption

  • desiredAccuracy: this is where the app tells LocationManager about what level of accuracy the app needs
  • distanceFilter: You can set desired distance between each pair of obtained locations in meters. LocationManager tries to provide the app with location information when the user has moved more than this distance.
1
2
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.distanceFilter = 5

After retrieving latitude, longitude you can use coordinate in your implementations as

1
2
let newItem = getCoordinateFromCoreData() // your retrieval function
let coordinate = CLLocationCoordinate2D(latitude: newItem.latitude, longitude: newItem.longitude)

GPX

The gpx(GPS eXchange Format) file ends with .gpx extension

  • wptType is an individual waypoint among a collection of points with no sequential relationship. It consists of the WGS 84 (GPS) coordinates of a point and possibly other descriptive information.
  • rteType is a route, an ordered list of routepoint (waypoints representing a series of significant turn or stage points) leading to a destination.[3]
  • trkType is a track, made of at least one segment containing waypoints, that is, an ordered list of points describing a path.[3] A Track Segment holds a list of Track Points which are logically connected in order. To represent a single GPS track where GPS reception was lost, or the GPS receiver was turned off, start a new Track Segment for each continuous span of track data.

Waypoints, routes and tracks recorded by GPS receivers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<gpx xmlns="http://www.topografix.com/GPX/1/1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
    version="1.1"
    creator="YourCompanyName">

     <wpt lat="Your latitude" lon="Your longitude">

       <time>Your Time</time>

       <name>Your Location name.</name>

    </wpt>

</gpx>

@FetchRequest as an extension

1
2
3
4
5
@FetchRequest(
  entity: TodoItem.entity(),
  sortDescriptors: [NSSortDescriptor(key: "dueDate", ascending: true)],
  predicate: NSPredicate(format: "dueDate < %@", Date.nextWeek() as CVarArg)
) var tasksDueSoon: FetchedResults<TodoItem>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// a convenient extension to set up the fetch request
extension TodoItem {
  static var dueSoonFetchRequest: NSFetchRequest<TodoItem> {
    let request: NSFetchRequest<TodoItem> = TodoItem.fetchRequest()
    request.predicate = NSPredicate(format: "dueDate < %@", Date.nextWeek() as CVarArg)
    request.sortDescriptors = [NSSortDescriptor(key: "dueDate", ascending: true)]

    return request
  }
}

// in your view
@FetchRequest(fetchRequest: TodoItem.dueSoonFetchRequest)
var tasksDueSoon: FetchedResults<TodoItem>

Errors

Must have a valid NSEntityDescription

First, check the NSPersistentContainer name you are using. It must match the .xcdatamodeld file name.

Second, check the App Code. Below code lines are important to initialise PersistenceController before using it. So that the entities are loaded.

TestCoreDataApp.swift
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import SwiftUI

@main
struct TestCoreDataApp: App {
    let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
    }
}