iOS SDK

Learn how to integrate the iOS SDK below. You can also see the source and a detailed SDK reference on GitHub.

Authentication

Authenticate using your publishable API keys, found on the Settings page. Use your Test Publishable key for testing and non-production environments. Use your Live Publishable key for production environments.

Note that you should always use your publishable API keys, which are restricted in scope, in the SDK. Do not use your secret API keys, which are unrestricted in scope, in any client-side code.

Configure Xcode project

To track the user's location in the foreground, you must add a string for the NSLocationWhenInUseUsageDescription key in your Info.plist file if you haven't already. This string will be displayed when prompting the user for foreground location permissions.

To track the user's location in the background, you must also add a string for the NSLocationAlwaysUsageDescription (iOS 10 and before) and NSLocationAlwaysAndWhenInUseUsageDescription (iOS 11 and later) keys in your Info.plist file if you haven't already. These strings will be displayed when prompting the user for background location permissions.

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Your iOS 11 and higher background location usage description goes here. e.g., "This app uses your location in the background to recommend places nearby."</string>

<key>NSLocationAlwaysUsageDescription</key>
<string>Your iOS 10 and lower background location usage description goes here. e.g., "This app uses your location in the background to recommend places nearby."</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>Your foreground location usage description goes here. e.g., "This app uses your location in the foreground to recommend places nearby."</string>

Then, in your project settings, go to Capabilities > Background Modes and turn on Background fetch. If using RadarTrackingOptions.responsive or RadarTrackingOptions.continuous you should also turn on Location updates. Note that this requires additional justification during App Store review. Learn more below.

<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>location</string>
</array>

Add iOS SDK to project

The best way to add the SDK to your project is via CocoaPods or Carthage.

The SDK is small and typically adds less than 500 KB to your compiled app.

CocoaPods

For CocoaPods, add the following to your Podfile:

pod 'RadarSDK', '~> 3.0.0'

Then, run pod install.

Carthage

To include Radar as a github origin in Carthage, add the following to your Cartfile:

github "radarlabs/radar-sdk-ios" ~> 3.0.0

To include Radar as a binary origin in Carthage, add the following to your Cartfile:

binary "https://raw.githubusercontent.com/radarlabs/radar-sdk-ios/master/RadarSDK.json" ~> 3.0.0

Then, run carthage update and drag Build/iOS/RadarSDK.framework into the Linked Frameworks and Libraries section of your target. Do not add the framework as an input to your copy-frameworks run script.

Add manually

You can also add the SDK to your project manually, though this is not recommended. Download the current release, unzip the package, and drag RadarSDK.framework into your Xcode project. It will automatically appear in the Linked Frameworks and Libraries section of your project settings.

Dependencies

The SDK depends on Apple's CoreLocation framework (for location services). In your project settings, go to General > Linked Frameworks and Libraries and add CoreLocation if you haven't already.

The SDK currently supports iOS 10 and higher.

Integrate iOS SDK into app

Initialize SDK

Import the SDK:

Swift Objective-C

import RadarSDK
// modules enabled
@import RadarSDK;

// modules not enabled
#import <RadarSDK/RadarSDK.h>

Initialize the SDK in your AppDelegate class, on the main thread, before calling any other Radar methods. In application:didFinishLaunchingWithOptions:application(_:didFinishLaunchingWithOptions:), call:

Swift Objective-C

Radar.initialize(publishableKey: publishableKey)
[Radar initializeWithPublishableKey:publishableKey];

where publishableKey is a string containing your publishable API key.

Identify user

Until you identify the user, Radar will automatically identify the user by deviceId (IDFV).

To identify the user when logged in, call:

Swift Objective-C

Radar.setUserId(userId)
[Radar setUserId:userId];

where userId is a stable unique ID for the user.

Do not send any PII, like names, email addresses, or publicly available IDs, for userId. See privacy best practices for more information.

To set an optional dictionary of custom metadata for the user, call:

Swift Objective-C

Radar.setMetadata(metadata)
[Radar setMetadata:metadata];

where metadata is a dictionary with up to 16 keys and values of type string, boolean, or number.

Finally, to set an optional description for the user, displayed in the dashboard, call:

Swift Objective-C

Radar.setDescription(description)
[Radar setDescription:description];

You only need to call these methods once, as these settings will be persisted across app sessions.

Request permissions

Radar respects standard iOS location permissions. Before tracking the user's location, the user must have authorized location permissions for the app if they haven't already.

To track the user's location in the foreground, the app's location authorization status must be kCLAuthorizationStatusAuthorizedWhenInUseauthorizedWhenInUse or kCLAuthorizationStatusAuthorizedAlwaysauthorizedAlways.

To track the user's location in the background, the app's location authorization status must be kCLAuthorizationStatusAuthorizedAlwaysauthorizedAlways.

Learn more about requesting permissions here.

Swift Objective-C

self.locationManager = CLLocationManager()

// foreground
self.locationManager.requestWhenInUseAuthorization()

// background
self.locationManager.requestAlwaysAuthorization()
self.locationManager = [CLLocationManager new];

// foreground
[self.locationManager requestWhenInUseAuthorization];

// background
[self.locationManager requestAlwaysAuthorization];

Foreground tracking

Once you have initialized the SDK, you have identified the user, and the user has authorized foreground permissions, you can track the user's location in the foreground.

To track the user's location in the foreground, call:

Swift Objective-C

Radar.trackOnce { (status: RadarStatus, location: CLLocation?, events: [RadarEvent]?, user: RadarUser?) in
  // do something with location, events, user
}
[Radar trackOnceWithCompletionHandler:^(RadarStatus status, CLLocation *location, NSArray<RadarEvent *> *events, RadarUser *user) {
  // do something with location, events, user
}];

You may provide an optional completionHandler that receives the request status, the user's location, the events generated, if any, and the user. The request status can be:

  • RadarStatusSuccesssuccess: success
  • RadarStatusErrorPublishableKeyerrorPublishableKey: SDK not initialized
  • RadarStatusErrorPermissionserrorPermissions: location permissions not granted
  • RadarStatusErrorLocationerrorLocation: location services error or timeout (10 seconds)
  • RadarStatusErrorNetworkerrorNetwork: network error or timeout (10 seconds)
  • RadarStatusErrorBadRequesterrorBadRequest: bad request (missing or invalid params)
  • RadarStatusErrorUnauthorizederrorUnauthorized: unauthorized (invalid API key)
  • RadarStatusErrorPaymentRequirederrorPaymentRequired: payment required (organization disabled or usage exceeded)
  • RadarStatusErrorForbiddenerrorForbidden: forbidden (insufficient permissions)
  • RadarStatusErrorNotFounderrorNotFound: not found
  • RadarStatusErrorRateLimiterrorRateLimit: too many requests (rate limit exceeded)
  • RadarStatusErrorServererrorServer: internal server error
  • RadarStatusErrorUnknownerrorUnknown: unknown error

Note that these calls are subject to rate limits.

Background tracking

Once you have initialized the SDK and the user has authorized background permissions, you can start tracking the user's location in the background.

For background tracking, the SDK supports custom tracking options as well as three presets:

  • RadarTrackingOptions.efficient: A preset that uses the iOS visit monitoring service to update only on stops and exits. Must move a significant distance to start moving again after a stop. Lowest battery usage.
  • RadarTrackingOptions.responsive: Updates about every 2.5 minutes when moving, shuts down when stopped, and only syncs stops and exits to the server. Requires the location background mode. Must move at least 200 meters to start moving again after a stop. Low battery usage.
  • RadarTrackingOptions.continuous: A preset that updates about every 30 seconds and syncs all locations to the server. High battery usage. Shows the flashing blue status bar when tracking.

Note that location updates may be delayed significantly by Low Power Mode, or if the device has connectivity issues, low battery, or wi-fi disabled.

To start tracking the user's location in the background, call one of:

Swift Objective-C

// efficient
Radar.startTracking(RadarTrackingOptions.efficient)

// responsive
Radar.startTracking(RadarTrackingOptions.responsive)

// continuous
Radar.startTracking(RadarTrackingOptions.continuous)
// efficient
[Radar startTrackingWithOptions:RadarTrackingOptions.efficient];

// responsive
[Radar startTrackingWithOptions:RadarTrackingOptions.responsive];

// continuous
[Radar startTrackingWithOptions:RadarTrackingOptions.continuous];

You only need to call this method once, as these settings will be persisted across app sessions.

Though we recommend using presets for most use cases, you can modify the presets. See the tracking options reference.

To stop tracking the user's location in the background (e.g., when the user logs out), call:

Swift Objective-C

Radar.stopTracking()
[Radar stopTracking];

Delegate

To listen for events, location updates, and errors client-side, create a class that implements RadarDelegate, then call setDelegate:setDelegate().

Set your RadarDelegate in a codepath that will be initialized and executed in the background. For example, make your AppDelegate implement RadarDelegate, not a ViewController. AppDelegate will be initialized in the background, whereas a ViewController may not be.

Swift Objective-C

class AppDelegate: UIResponder, UIApplicationDelegate, RadarDelegate {

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    Radar.initialize(publishableKey: publishableKey)
    Radar.setDelegate(self)

    return true
  }

  func didReceiveEvents(_ events: [RadarEvent], user: RadarUser) {
    // do something with events, user
  }

  func didUpdateLocation(_ location: CLLocation, user: RadarUser) {
    // do something with location, user
  }

  func didUpdateClientLocation(_ location: CLLocation, stopped: Bool, source: RadarLocationSource) {
    // do something with location, stopped, source
  }

  func didFail(status: RadarStatus) {
    // do something with status
  }

}
@interface AppDelegate : UIResponder <UIApplicationDelegate, RadarDelegate>

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [Radar initializeWithPublishableKey:publishableKey];
  [Radar setDelegate:self];

  return YES;
}

- (void)didReceiveEvents:(NSArray<RadarEvent *> *)events user:(RadarUser *)user {
  // do something with events, user
}

- (void)didUpdateLocation:(CLLocation *)location user:(RadarUser *)user {
  // do something with location, user
}

- (void)didUpdateClientLocation:(CLLocation *)location stopped:(BOOL)stopped source:(RadarLocationSource)source {
  // do something with location, stopped, source
}

- (void)didFailWithStatus:(RadarStatus)status {
  // do something with status
}

@end

To listen for events server-side instead, add a webhook.

Manual tracking

If you want to manage location services yourself, you can manually update the user's location instead by calling:

Swift Objective-C

Radar.trackOnce(
  location: location
) { (status: RadarStatus, location: CLLocation?, events: [RadarEvent]?, user: RadarUser?) in
  // do something with location, events, user
}
[Radar trackOnceWithLocation:location
           completionHandler:^(RadarStatus status, CLLocation *location, NSArray<RadarEvent *> *events, RadarUser *user) {
  // do something with location, events, user
}];

where location is a CLLocation instance with a valid latitude, longitude, and horizontal accuracy.

Note that these calls are subject to rate limits.

Verify events

You can accept or reject events after user check-ins or other forms of verification. Event verifications will be used to improve the accuracy and confidence level of future Insights and Places events.

Swift Objective-C

Radar.acceptEventId(event._id, verifiedPlaceId: event.alternatePlaces[0]._id)

Radar.rejectEventId(event._id)
[Radar acceptEventId:event._id withVerifiedPlaceId:event.alternatePlaces[0]._id];

[Radar rejectEventId:event._id];

Submit to App Store

Apple requires that you justify your use of background location. Add something materially similar to the following to the bottom of your App Store description: This app uses background location to (insert use case here). Continued use of background location may decrease battery life.

If you turned on the Location updates background mode, Apple requires additional justification in your App Store review notes. If using RadarTrackingOptions.responsive, add something like: This app uses the Radar SDK (https://radar.io) to (insert use case here). The Radar SDK requires the background location mode to support polygon geofences and nearby place detection, which cannot be accomplished with region monitoring or visit monitoring. Or, if using RadarTrackingOptions.continuous, add something like This app uses the Radar SDK (https://radar.io) to (insert use case here). The Radar SDK requires the location background mode for live trip tracking and live ETAs.

Learn more about this requirement in section 2.5.4 of the App Store Review Guidelines here.

Other APIs

The iOS SDK also exposes APIs for anonymous context, geocoding, search, and distance.

Get location

Get a single location update without sending it to the server:

Swift Objective-C

Radar.getLocation { (status: RadarStatus, location: CLLocation?, stopped: Bool) in
  // do something with location
}
[Radar getLocationWithCompletionHandler:^(RadarStatus status, CLLocation *location, BOOL stopped) {
  // do something with location
}];

Context

With the context API, get context for a location without sending device or user identifiers to the server:

Swift Objective-C

Radar.getContext { (status: RadarStatus, location: CLLocation?, context: RadarContext?) in
  // do something with context
}
[Radar getContextWithCompletionHandler:^(RadarStatus status, CLLocation *location, RadarContext *context) {
  // do something with context
}];

Geocoding

With the forward geocoding API, geocode an address, converting address to coordinates:

Swift Objective-C

Radar.geocode(
  address: "20 jay st brooklyn ny"
) { (status, addresses) in
  // do something with addresses
}
[Radar geocodeAddress:@"20 jay st brooklyn ny"
    completionHandler:^(RadarStatus status, NSArray<RadarAddress *> *addresses) {
  // do something with addresses
}];

With the reverse geocoding API, reverse geocode a location, converting coordinates to address:

Swift Objective-C

Radar.reverseGeocode(
  location: location
) { (status, addresses) in
  // do something with addresses
}
[Radar reverseGeocodeLocation:location
            completionHandler:^(RadarStatus status, NSArray<RadarAddress *> *addresses) {
  // do something with addresses
}];

With the IP geocoding API, geocode the device's current IP address, converting IP address to city, state, and country:

Swift Objective-C

Radar.ipGeocode { (status, address) in
  // do something with address
}
[Radar ipGeocodeWithCompletionHandler:^(RadarStatus status, RadarAddress *address) {
  // do something with address
}];

With the autocomplete API, autocomplete partial addresses and place names, sorted by relevance:

Swift Objective-C

Radar.autocomplete(
  query: "brooklyn roasting",
  near: location,
  limit: 10
) { (status, addresses) in
  // do something with addresses
}
[Radar autocompleteQuery:@"brooklyn roasting"
                    near:location
                   limit:10
       completionHandler:^(RadarStatus status, NSArray<RadarAddress *> *addresses) {
  // do something with addresses
}];

With the geofence search API, search for geofences near a location, sorted by distance:

Swift Objective-C

Radar.searchGeofences(
  near: location,
  radius: 1000, // meters
  tags: ["store"],
  metadata: nil,
  limit: 10
) { (status, location, geofences) in
  // do something with geofences
}
[Radar searchGeofencesNear:location
                    radius:1000 // meters
                      tags:@[@"store"]
                  metadata:nil
                     limit:100
         completionHandler:^(RadarStatus status, CLLocation *location, NSArray<RadarGeofence *> *geofences) {
  // do something with geofences
}];

With the places search API, search for places near a location, sorted by distance:

Swift Objective-C

Radar.searchPlaces(
  near: location,
  radius: 1000, // meters
  chains: ["starbucks"],
  categories: nil,
  groups: nil,
  limit: 10
) { (status, location, places) in
  // do something with places
}
[Radar searchGeofencesNear:location
                    radius:1000 // meters
                    chains:@[@"starbucks"]
                categories:nil
                    groups:nil
                     limit:100
         completionHandler:^(RadarStatus status, CLLocation *location, NSArray<RadarPlace *> *places) {
  // do something with places
}];

Distance

With the distance API, calculate the travel distance and duration between two locations:

Swift Objective-C

Radar.getDistance(
  origin: origin,
  destination: destination,
  modes: [.foot, .car],
  units: .imperial
) { (status, routes) in
  // do something with routes
}
[Radar getDistanceFromOrigin:origin
                 destination:destination
                       modes:RadarRouteModeFoot | RadarRouteModeCar
                       units:RadarRouteUnitsImperial
           completionHandler:^(RadarStatus status, RadarRoutes *routes) {
  // do something with routes
}];