Install SDK on iOS

Add HyperTrack SDK to your Podfile

We use CocoaPods to distribute the SDK, you can install it here.

Using command line run pod init in your project directory to create a Podfile. Put the following code (changing target placeholder to your target name) in the Podfile:

platform :ios, '9.0'
inhibit_all_warnings!
target 'YourApp' do
use_frameworks!
pod 'HyperTrack', '4.0.1'
end

Run pod install. CocoaPods will build the dependencies and create a workspace (.xcworkspace) for you.

If your project uses Objective-C only, you need to configure SWIFT_VERSION in your project's Build Settings. Alternatively, you can create an empty Swift file, and Xcode will create this setting for you.

If you are using Xcode 10.1, which doesn't support Swift 5, add this post_install script at the bottom of your Podfile:

Show code block
post_install do |installer|
installer.pods_project.targets.each do |target|
if ['GRDB.swift'].include? target.name
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '4.2'
end
end
end
end

Enable background location updates

Enable Background Modes in your project target's Capabilities tab. Choose "Location updates".

Capabilities tab in Xcode

Handle location and motion permissions

Set the following purpose strings in the Info.plist file:

Always authorization location

HyperTrack SDK requires "Always" permissions to reliably track user's location. Be advised, purpose strings are mandatory.

Your app needs to make sure that it has location and motion permissions for location tracking to work. See this F.A.Q. page for details on permissions best practices.

Initialize the SDK

Get your publishable key from the Setup page.

Put the initialization call inside your AppDelegate's application:didFinishLaunchingWithOptions: method:

Swift
let publishableKey = HyperTrack.PublishableKey("PASTE_YOUR_PUBLISHABLE_KEY_HERE")!
switch HyperTrack.makeSDK(publishableKey: publishableKey) {
case let .success(hyperTrack):
// Use `hyperTrack` instance
case let .failure(fatalError):
// Handle errors, for example using switch
}
Objective-C

Import the SDK:

@import HyperTrack;

Initialize the SDK.

NSString *publishableKey = @"PASTE_YOUR_PUBLISHABLE_KEY_HERE";
HTResult *result = [HTSDK makeSDKWithPublishableKey:publishableKey];
if (result.hyperTrack != nil) {
// Use `hyperTrack` instance from `result.hyperTrack`
} else {
// Handle errors, for example using switch:
switch ([result.error code]) {
case HTFatalErrorProductionLocationServicesUnavalible:
case HTFatalErrorProductionMotionActivityServicesUnavalible:
// Handle a case where device is fully untrackable (either iPhone 5 or lower
// or not an iPhone
break;
case HTFatalErrorProductionMotionActivityPermissionsDenied:
// Handle motion permissions denied error. Enabling permissions will
// restart the app
default:
// Other errors should only happen during development
break;
}
}

NSNotifications

Restorable and Unrestorable error notifications are called if the SDK encounters an error that prevents it from tracking. SDK can recover in runtime from Restorable errors if the error reason is resolved. Errors include:

  • Initialization errors, like denied Location or Motion permissions (RestorableError.locationPermissionsDenied)
  • Authorization errors from the server. If the trial period ends and there is no credit card tied to the account, this is the error that will be called (RestorableError.trialEnded)
  • Incorrectly typed Publishable Key (UnrestorableError.invalidPublishableKey)
Swift
If you want to handle errors using the same selector:
NotificationCenter.default.addObserver(
self,
selector: #selector(trackingError(notification:)),
name: HyperTrack.didEncounterUnrestorableErrorNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(trackingError(notification:)),
name: HyperTrack.didEncounterRestorableErrorNotification,
object: nil
)
...
@objc func trackingError(notification: Notification) {
if let trackingError = notification.hyperTrackTrackingError() {
// Handle TrackingError, which is an enum of Restorable or Unrestorable error
}
}
If you want to handle errors separately, or handle only Restorable or only Unrestorable errors:
NotificationCenter.default.addObserver(
self,
selector: #selector(unrestorableError(notification:)),
name: HyperTrack.didEncounterUnrestorableErrorNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(restorableError(notification:)),
name: HyperTrack.didEncounterRestorableErrorNotification,
object: nil
)
...
@objc func restorableError(notification: Notification) {
if let restorableError = notification.hyperTrackRestorableError() {
// Handle RestorableError
}
}
@objc func unrestorableError(notification: Notification) {
if let unrestorableError = notification.hyperTrackUnrestorableError() {
// Handle UnrestorableError
}
}
Objective-C
If you want to handle errors using the same selector:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hyperTrackEncounteredTrackingError:)
name:HTSDK.didEncounterRestorableErrorNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hyperTrackEncounteredTrackingError:)
name:HTSDK.didEncounterUnrestorableErrorNotification
object:nil];
...
- (void)hyperTrackEncounteredTrackingError:(NSNotification *)notification {
// Use tracking error helper
NSError *error = [notification hyperTrackTrackingError];
if (error != nil) {
if ([[error domain] isEqualToString:NSError.HTRestorableErrorDomain]) {
// Handle restorable error
} else if ([[error domain] isEqualToString:NSError.HTUnrestorableErrorDomain]) {
// Handle unrestorable error
}
}
}
If you want to handle errors separately, or handle only Restorable or only Unrestorable errors:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hyperTrackEncounteredRestorableError:)
name:HTSDK.didEncounterRestorableErrorNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hyperTrackEncounteredUnrestorableError:)
name:HTSDK.didEncounterUnrestorableErrorNotification
object:nil];
...
- (void)hyperTrackEncounteredRestorableError:(NSNotification *)notification {
NSError *restorableError = [notification hyperTrackRestorableError]);
// Handle RestorableError
}
- (void)hyperTrackEncounteredUnrestorableError:(NSNotification *)notification {
NSError *unrestorableError = [notification hyperTrackUnrestorableError]);
// Handle UnrestorableError
}

You can also observe when SDK starts and stops tracking and update the UI:

Swift
NotificationCenter.default.addObserver(
self,
selector: #selector(self.trackingStarted),
name: HyperTrack.startedTrackingNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(self.trackingStopped),
name: HyperTrack.stoppedTrackingNotification,
object: nil
)
Objective-C
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(trackingStarted)
name:HTSDK.startedTrackingNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(trackingStopped)
name:HTSDK.stoppedTrackingNotification
object:nil];

Enable remote notifications

The SDK has a bi-directional communication model with the server. This enables the SDK to run on a variable frequency model, which balances the fine trade-off between low latency tracking and battery efficiency, and improves robustness. For this purpose, the iOS SDK uses APNs silent remote notifications.

This guide assumes you have configured APNs in your application. If you haven't, read the iOS documentation on APNs.

Configure APNs on the dashboard

Log into the HyperTrack dashboard, and open the setup page. Upload your Auth Key (file in the format AuthKey_KEYID.p8) and fill in your Team ID.

This key will only be used to send silent push notifications to your apps.

Enable remote notifications in the app

In the app capabilities, ensure that remote notifications inside background modes is enabled.

Remote Notifications in Xcode

In the same tab, ensure that push notifications is enabled.

Push Notifications in Xcode

Registering and receiving notifications

The following changes inside AppDelegate will register the SDK for push notifications and route HyperTrack notifications to the SDK.

Register for notifications

Inside didFinishLaunchingWithOptions, use the SDK method to register for notifications.

Swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
HyperTrack.registerForRemoteNotifications()
return true
}
Objective-C
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[HTSDK registerForRemoteNotifications];
return YES;
}

Register device token

Inside and didRegisterForRemoteNotificationsWithDeviceToken and didFailToRegisterForRemoteNotificationsWithError methods, add the relevant lines so that HyperTrack can register the device token.

Swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
HyperTrack.didRegisterForRemoteNotificationsWithDeviceToken(deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
HyperTrack.didFailToRegisterForRemoteNotificationsWithError(error)
}
Objective-C
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[HTSDK didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[HTSDK didFailToRegisterForRemoteNotificationsWithError:error];
}

Receive notifications

Inside the didReceiveRemoteNotification method, add the HyperTrack receiver. This method parses only the notifications sent from HyperTrack.

Swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
HyperTrack.didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)
}
Objective-C
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[HTSDK didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

If you want to make sure to only pass HyperTrack notifications to the SDK, you can use the "hypertrack" key:

Swift
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if userInfo["hypertrack"] != nil {
// This is HyperTrack's notification
HyperTrack.didReceiveRemoteNotification(userInfo, fetchCompletionHandler: completionHandler)
} else {
// Handle your server's notification here
}
}
Objective-C
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if (userInfo[@"hypertrack"] != nil) {
// This is HyperTrack's notification
[HTSDK didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
} else {
// Handle your server's notification here
}
}

Start tracking

Now the app is ready to be tracked from the cloud. HyperTrack gives you powerful APIs to control device tracking from your backend.

To use the HyperTrack API, you will need the {AccountId} and {SecretKey} from the Setup page.

Track devices during work

Track devices when user is logged in to work, or during work hours by calling the Devices API.

To start, call the start API.

curl -X POST \
-u {AccountId}:{SecretKey} \
https://v3.api.hypertrack.com/devices/{device_id}/start

Get the tracking status of the device by calling GET /devices/{device_id} api.

curl \
-u {AccountId}:{SecretKey} \
https://v3.api.hypertrack.com/devices/{device_id}

To see the device on a map, open the returned embed_url in your browser (no login required, so you can add embed these views directly to you web app). The device will also show up in the device list in the HyperTrack dashboard.

To stop tracking, call the stop API.

curl -X POST \
-u {AccountId}:{SecretKey} \
https://v3.api.hypertrack.com/devices/{device_id}/stop

Track trips with ETA

If you want to track a device on its way to a destination, call the Trips API and add destination.

HyperTrack Trips API offers extra fields to get additional intelligence over the Devices API.

  • set destination to track route and ETA
  • set scheduled_at to track delays
  • share live tracking URL of the trip with customers
  • embed live tracking view of the trip in your ops dashboard
curl -u {AccountId}:{SecretKey} --location --request POST 'https://v3.api.hypertrack.com/trips/' \
--header 'Content-Type: application/json' \
--data-raw '{
"device_id": "{device_id}",
"destination": {
"geometry": {
"type": "Point",
"coordinates": [{longitude}, {latitude}]
}
}
}'

To get {longitude} and {latitude} of your destination, you can use for example Google Maps.

HyperTrack uses GeoJSON. Please make sure you follow the correct ordering of longitude and latitude.

The returned JSON includes the embed_url for your dashboard and share_url for your customers.

When you are done tracking this trip, call complete Trip API using the trip_id from the create trip call above.

curl -X POST \
-u {AccountId}:{SecretKey} \
https://v3.api.hypertrack.com/trips/{trip_id}/complete

After the trip is completed, use the Trips API to retrieve a full summary of the trip. The summary contains the polyline of the trip, distance, duration and markers of the trip.

curl -X POST \
-u {AccountId}:{SecretKey} \
https://v3.api.hypertrack.com/trips/{trip_id}

Track trips with geofences

If you want to track a device going to a list of places, call the Trips API and add geofences. This way you will get arrival, exit, time spent and route to geofences. Please checkout our docs for more details.

Dashboard

Once your app is running, go to the dashboard where you can see a list of all your devices and their live location with ongoing activity on the map.

Optional steps

Identify devices

All devices tracked on HyperTrack are uniquely identified using UUID. You can get this identifier programmatically in your app by calling deviceID after initialization. Another approach is to tag device with a name that will make it easy to distinguish them on HyperTrack Dashboard.

Swift
hyperTrack.setDeviceName("Device name")
Objective-C
hyperTrack.deviceName = @"Device name";

You can additionaly tag devices with custom metadata. Metadata should be representable in JSON.

Swift
if let metadata = HyperTrack.Metadata(rawValue: ["key": "value"]) {
hyperTrack.setDeviceMetadata(metadata)
} else {
// Metadata can't be represented in JSON
}
Objective-C
NSDictionary *dictionary = @{@"key": @"value"};
HTMetadata *metadata = [[HTMetadata alloc] initWithDictionary:dictionary];
if (metadata != nil) {
[self.hyperTrack setDeviceMetadata:metadata];
} else {
// Metadata can't be represented in JSON
}

Add a trip marker

Use this optional method if you want to tag the tracked data with trip markers that happen in your app. E.g. user marking a task as done, user tapping a button to share location, user accepting an assigned job, device entering a geofence, etc.

The process is the same as for device metadata:

Swift
if let metadata = HyperTrack.Metadata(rawValue: ["status": "PICKING_UP"]) {
hyperTrack.addTripMarker(metadata)
} else {
// Metadata can't be represented in JSON
}
Objective-C
NSDictionary *dictionary = @{@"status": @"PICKING_UP"};
HTMetadata *metadata = [[HTMetadata alloc] initWithDictionary:dictionary];
if (metadata != nil) {
[self.hyperTrack addTripMarker:metadata];
} else {
// Metadata can't be represented in JSON
}

Frequently Asked Questions

Error: Access to Activity services has not been authorized

You are running the quickstart app on the iOS simulator, which currently does not support CoreMotion services. You can test the app on real iOS devices only.

What are the best practices for handling permissions on iOS?

In Human Interface Guidelines Apple recommends:

  • Requesting permissions only when they are needed in the flow of the app. If you app is centered around location tracking, then asking for permissions at the app launch can be understandable for users. On the other hand, if location tracking is just one of the features, then it makes sense to request them only when the feature is activated.
  • Providing short and specific purpose string. Purpose string should explain the value that location and motion tracking provides. Examples of motion tracking benefits: improves battery life by using algorithms based on motion tracking data, provides story-like details for historical tracking data, gives live feedback on current activity.

In addition a lot of great apps provide a special screen explaining the need for permissions before asking them. If permissions are denied you can guide the user to the specific page in the Settings.app to change permissions (see this guide for special deep-links for the Settings.app).

On iOS 13 Apple introduced a new "Provisional Always" authorization state (see this StackOverflow answer for details). In short:

  • there is no API to detect this state
  • during this state there are no location events in background
  • user sees his permissions as granted and sees "While Using" state in Settings.app
  • app sees permissions as granted with "Always" state.

HyperTrack is working on ways to detect this state and provide APIs that would enable app developers to display explanation screens that will guide the user back to Settings.app to switch permissions from "While Using" to "Always".