iOS - Getting Started

Minumum Operating System

8.0+

Get Pilgrim Access

Developers can get access to the free tier of the SDK by following these three steps:

  1. Create a developer account.
  2. Complete the free tier application by creating a new app or request access for an existing app.
  3. Expect a response within 7 business days.

If you have more than 100k active users with location permissions enabled, let’s chat!


Register Your App’s Bundle IDs

Important: In order for Pilgrim SDK to authenticate with our server, you’ll need to add your iOS Bundle ID to your Foursquare app’s configuration. Navigate to your Foursquare Developer Console and select your Pilgrim app: foursquare.com/developers/apps/CLIENT_ID

  1. Find your app’s Bundle Indentifier. This can be found in the Identity section of your project’s General tab: Screenshot showing where to adding iOS Bundle ID

  2. Near the bottom of the App Settings page, under Install options, paste your iOS Bundle ID in the iOS Bundle IDs field. Screenshot showing where to set the iOS Bundle ID in your Foursquare Developer Console.
    Note: you can add multiple bundle IDs delimited by commas.

  3. Save your changes.


Installation

If you are upgrading from a previous version of Pilgrim SDK that used a .netrc file, please take a look at these additional steps that you may need to complete.

  1. Add the following to your Cartfile:
  1. Navigate to Carthage/Build/iOS and drag the Pilgrim.framework file into Xcode in the Link Binary With Libraries section.
  2. Add a run script with the following script:

Set the Input Files to:

  1. Load the Pilgrim library into any necessary files by adding import Pilgrim.
  2. Build your project.
  1. If you don’t already have CocoaPods initated in your project, enter the following command into the terminal: pod init
  2. Add the following to your Podfile:
  1. Enter the following command into the terminal: pod install (If you experience errors, try pod update. You can read about the difference here.)
  2. Load the Pilgrim library into any necessary files by adding import Pilgrim.
  3. Build your project.
  1. Download and unzip the latest release.
  2. Embed the framework. Drag Pilgrim.framework into your project’s Embedded Binaries section in the project editor. In the sheet that appears, make sure “Copy items if needed” is checked, then click Finish.
  3. Load the Pilgrim library into any necessary files by adding import Pilgrim.
  4. Under the Build Phases tab, add this run script (for manual installs only):
  1. Build your project.

Permissions

Make sure to turn Background Modes to On in your project’s Capabilities tab and enable the Location updates checkbox:

screenshot showing iOS Background modes

Add the following to your iOS permission strings in your project’s Info.plist file:

  • Privacy - Location Always Usage Description
  • Privacy - Location Always and When In Use Usage Description
  • Privacy - Location When In Use Usage Description

For example:

screenshot showing iOS privacy permissions


Configure and Start Pilgrim

1. Configure Pilgrim in your AppDelegate

Configure Pilgrim by pasting the following code in the didFinishLaunchingWithOptions method of your AppDelegate. Be sure to replace CLIENT_ID and CLIENT_SECRET with your real API credentials.

PilgrimManager.shared().configure(withConsumerKey: "CLIENT_ID", secret: "CLIENT_SECRET", delegate: self, completion: nil)

Common Mistake: Why must Pilgrim’s configuration live in the AppDelegate’s didFinishLaunchingWithOptions and not somewhere else like a view controller? It relates to how Pilgrim functions within iOS’s app life cycle. Pilgrim works in the background and the didFinishLaunchingWithOptions method is the ONLY part of the app’s life cycle that is guaranteed to be called, even when the app is in the background. When you put Pilgrim’s configuration and notification handlers in another location, Pilgrim only gets called when that app is active, meaning you miss a ton of your user’s visit activity.

2. Conform to Pilgrim Delegate

Have your AppDelegate conform PilgrimManagerDelegate by pasting the following code just below your AppDelegate class:

extension AppDelegate : PilgrimManagerDelegate {
  // Primary visit handler:
  func pilgrimManager(_ pilgrimManager: PilgrimManager, handle visit: Visit) {
    // Process the visit however you'd like:
    print("\(visit.hasDeparted ? "Departure from" : "Arrival at") \(visit.venue != nil ? visit.venue!.name : "Unknown venue."). Added a Pilgrim visit at: \(visit.displayName)")
  }

  // Optional: If visit occurred without network connectivity
  func pilgrimManager(_ pilgrimManager: PilgrimManager, handleBackfill visit: Pilgrim.Visit) {
    // Process the visit however you'd like:
    print("Backfill \(visit.hasDeparted ? "departure from" : "arrival at") \(visit.venue != nil ? visit.venue!.name : "Unknown venue."). Added a Pilgrim backfill visit at: \(visit.displayName)")
  }

  // Optional: If visit occurred by triggering a geofence
  func pilgrimManager(_ pilgrimManager: PilgrimManager, handle geofenceEvents: [GeofenceEvent]) {
    // Process the geofence events however you'd like. Here we loop through the potentially multiple geofence events and handle them individually:
    geofenceEvents.forEach { geofenceEvent in
      print(geofenceEvent)
    }
  }
}

Note the three delegate callbacks you may receive:

  • handle visit: The primary visit callback that receives arrival and departure events.
  • handleBackfill visit: This callback receives visits that occurred historically when there was no network connectivity or for failed visits that have been retried.
  • handle geofenceEvents: This callback receives any genfence events configured in the Pilgrim console.

3. Request Location Permissions + Start Pilgrim

Once Pilgrim is configured and set up to handle locations events, you just need to request location permissions from your user and tell Pilgrim to start running by calling start when .authorizedAlways:

PilgrimManager.shared().start()

Note: You must make sure the user has provided access to background location permissions before starting the SDK. It is your responsibility as a developer to inform the user of how you are using these permissions and how it benefits them.

To help you maximize the number of users that opt into “Always” location sharing and your Pilgrim SDK powered features, here are some recommendations for how you should handle requesting permissions:

  • Ask for location permission only when a user accesses a feature that uses location.
  • Make sure it’s clear, in the app, why the user should provide access to their location and how it benefits their app experience.
  • After asking for location permissions in the app, consider adding a screen that informs the user that they will be later asked for “Always” permission. This screen should clearly explain the value the user gains by providing “Always” location.
  • If the user declines to give “Always” permission, consider places in your app that you can promote features that make use of background location.
  • Lastly, we expect that most or all of your existing users that are on iOS 13 will see a prompt displaying a map of recent locations, and an option to “Change to While Using”. Please review your “NSLocationAlwaysAndWhenInUseUsageDescription” string in your plist to ensure it clearly states the feature and value to the user.

4. Testing a Visit

With Pilgrim properly configured, the delegate method(s) should be hit whenever you arrive or depart at one of our 105M+ places around the world. In order to test your visit callback without physically walking around, we provide a testing class visitTester. This class can be used to simulate visits with different confidence levels and location types.

For example, the following code will simulate a visit at a given lat/lng:

PilgrimManager.shared().visitTester.fireTestVisit(location: CLLocation(latitude: 37.7904311, longitude: -122.4066613))

The following code will trigger a medium confidence arrival at a venue:

PilgrimManager.shared().visitTester.fireTestVisit(confidence: .medium, type: .venue, departure: false)

Foreground Location

Pilgrim’s traditional visit detection works effortlessly in the background, allowing continued interaction with your users regardless of if they are in your app or not. But what about when you want to manually find a user’s current location while they are interacting within your app? Pilgrim allows you to actively request the device’s current location manually when the app is in use by calling the below method:

PilgrimManager.shared().getCurrentLocation { (currentLocation, error) in
   // Example: currentLocation.currentPlace.venue.name
}
[[FSQPPilgrimManager sharedManager] getCurrentLocationWithCompletion:^(FSQPCurrentLocation * _Nullable currentLocation, NSError * _Nullable error) {
   // Example: currentLocation.currentPlace.venue.name
}];

Note: This method can be used with users that have only given ‘When In Use’ location permisions and is a great workaround to satisfy our SDK’s ‘Always On’ location permission requirement.

This will return the current venue the device is most likely at (in the currentLocation object), as well as any geofences that the device is in (if configured). Note that this foregoes any “stop detection” so it shouldn’t be used as a proxy for visits. For example, this method could return a high confidence result for Starbucks if called while someone is walking by the entrance.

Reasons for a nil or error response

There are a couple reasons why calling getCurrentLocation would return an error or a nil value:

  1. Lack of network connectivity: The most likely reason the SDK cannot retrieve the device’s current location would be because the API request has failed to make a proper connection to the Foursquare server.
  2. Lack of location permissions: If the LocationSubscriber is not allowed to get location info. This means that the proper location permissions have not been properly set or granted by the user. For foreground usage, the When In Use permissions are required. And if being used in the background, it will require the Location Always permission.
  3. CLLocation timeout: If CLLocation doesn’t return a location to us within the timeout value we specify and fails to resolve a location.

Next Steps

API Reference Docs

Implementation Checklist

Configuration

Android - Getting Started

Minumum Operating System

Android v16+ (Jelly Bean)

Get Pilgrim Access

Developers can get access to the free tier of the SDK by following these three steps:

  1. Create a developer account.
  2. Complete the free tier application by creating a new app or request access for an existing app.
  3. Expect a response within 7 business days.

If you have more than 100k active users with location permissions enabled, let’s chat!


Register Your App’s Key Hashes

Important: In order for Pilgrim SDK to authenticate with our server, you’ll need to add your app’s Android Key Hash to your app’s Foursquare configuration settings. Navigate to your Foursquare Developer Console and select your Pilgrim app: foursquare.com/developers/apps/CLIENT_ID

  1. Generate a key hash of your developer certificate using this command:
    keytool -list -v -keystore [your keystore file path]
    

    For example, your debug key for development might look like:

    keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
    
  2. Near the bottom of the page, under Install options, paste your Android SHA1 Key Hash in the Android Key Hashes field: Screenshot showing where to set the Android key hash in your Foursquare Developer Console.
    Note: you can add multiple key hashes delimited by commas.

  3. Save your changes.

Installation

1. Adding the dependency

Once you have been given a username and password from Foursquare, you can access the repository that has the Pilgrim SDK. Add the following snippet to your root build.gradle file and you will be able to resolve the library.

allprojects {
    repositories {
        maven {
            url 'https://foursquare.jfrog.io/foursquare/libs-release/'
        }
    }
}

Then, in the build.gradle file for the project for which you would like to include the Pilgrim SDK, add:

implementation 'com.foursquare:pilgrimsdk:2.2.0'

Permissions

The following permissions are automatically added to your manifest file when you import the Pilgrim SDK library:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

Please let us know if you have any questions about this list. An example AndroidManifest.xml file can be found here.


Configure and Start Pilgrim

1. Configure the Pilgrim SDK

Use the following code to configure the Pilgrim SDK notification handler in your Application’s onCreate method. Also, you can optionally set the log level to DEBUG to receive more detailed logs while you’re developing and persist them on disk.

PilgrimSdk.Builder builder = new PilgrimSdk.Builder(this)
        .consumer("CLIENT_ID", "CLIENT_SECRET")
        .notificationHandler(pilgrimNotificationHandler)
        .logLevel(LogLevel.DEBUG);
PilgrimSdk.with(builder);
PilgrimSdk.with(
    PilgrimSdk.Builder(this)
        .consumer("CLIENT_ID", "CLIENT_SECRET")
        .notificationHandler(pilgrimNotificationHandler)
        .logLevel(LogLevel.DEBUG)
)

Common Mistake: Why must Pilgrim’s configuration live in the Application’s onCreate and not in an activity? It relates to how Pilgrim functions within Android’s app life cycle. Pilgrim works in the background and the Application’s onCreate is the ONLY part of the app’s life cycle that is guaranteed to be called, even when the app is in the background. When you put Pilgrim’s configuration and notification handlers in an activity, Pilgrim only gets called when that app is active, meaning you will miss user activity when the app is relaunched from the background.

2. Set up the Pilgrim Notification Handler

In order to be notified of visits, add the Pilgrim SDK notification handlers to your Application’s onCreate method.

private final PilgrimNotificationHandler pilgrimNotificationHandler = new PilgrimNotificationHandler() {
    // Primary visit handler
    @Override
    public void handleVisit(Context context, PilgrimSdkVisitNotification notification) {
        // Process the visit however you'd like:
        Visit visit = notification.getVisit();
        Venue venue = visit.getVenue();
        Log.d("PilgrimSdk", visit.toString());
    }

    // Optional: If visit occurred while in Doze mode or without network connectivity
    @Override
    public void handleBackfillVisit(Context context, PilgrimSdkBackfillNotification notification) {
        // Process the visit however you'd like:
        super.handleBackfillVisit(context, notification);
        Visit visit = notification.getVisit();
        Venue venue = visit.getVenue();
        Log.d("PilgrimSdk", visit.toString());

    }

    // Optional: If visit occurred by triggering a geofence
    @Override
    public void handleGeofenceEventNotification(Context context, PilgrimSdkGeofenceEventNotification notification) {
        // Process the geofence events however you'd like:
        List<GeofenceEvent> geofenceEvents = notification.getGeofenceEvents();
        for (GeofenceEvent geofenceEvent : geofenceEvents) {
          Log.d("PilgrimSdk", geofenceEvent.toString());
        }
    }
};
private val pilgrimNotificationHandler = object : PilgrimNotificationHandler() {
    // Primary visit handler
    override fun handleVisit(context: Context, notification: PilgrimSdkVisitNotification) {
        val visit = notification.visit
        val venue = visit.venue
        Log.d("PilgrimSdk", visit.toString())
    }

    // Optional: If visit occurred while in Doze mode or without network connectivity
    override fun handleBackfillVisit(context: Context, notification: PilgrimSdkBackfillNotification) {
        val visit = notification.visit
        val venue = visit.venue
        Log.d("PilgrimSdk", visit.toString())
    }

    // Optional: If visit occurred by triggering a geofence
    override fun handleGeofenceEventNotification(context: Context, notification: PilgrimSdkGeofenceEventNotification) {
        super.handleGeofenceEventNotification(context, notification)
        // Process the geofence events however you'd like. Here we loop through the potentially multiple geofence events and handle them individually:
        notification.geofenceEvents.forEach { geofenceEvent ->
            Log.d("PilgrimSdk", geofenceEvent.toString())
        }
    }
}

Note the types of notifications you may receive:

  • handleVisit: The primary visit handler that receives arrival and departure events.
  • handleBackfillVisit: This handler receives visits that occurred when theire was no network connectivity or for failed visits that have been retried. For arrivals, departureTime will be null.
  • handleGeofenceEventNotification: This handler receives visits for geofences.

3. Request Location Permissions + Start Pilgrim

Once Pilgrim is configured and set up to handle events, you just need to request location permissions from your user and tell Pilgrim to start running by calling start when ACCESS_FINE_LOCATION has been granted:

PilgrimSdk.start(this);

Note: You must make sure the user has provided access to location permissions before starting the SDK. It is your responsibility as a developer to inform the user of how you are using these permissions and how it benefits them.

To help you maximize the number of users that opt into location sharing and your Pilgrim SDK powered features, here are some recommendations for how you should handle requesting permissions:

  • For newer versions of Android, ask for location permission only when a user accesses a feature that uses location.
  • Make sure it’s clear, in the app, why the user should provide access to their location and how it benefits their app experience.
  • If the user declines to give permission, consider places in your app that you can promote features that make use of background location.

4. Testing a Visit

With Pilgrim properly configured, the notification handlers hit whenever you arrive or depart one of our 105M+ places around the world. In order to test your visit callback without physically walking around, we provide a test method:

  • PilgrimNotificationTester.sendTestVisitArrivalAtLocation can test your PilgrimNotificationHandler implementation without moving around. By passing a latitude and longitude, this method communicates with the Foursquare servers and sends back a notification if the location matches your configuration.
    void PilgrimNotificationTester.sendTestVisitArrivalAtLocation(
          @NonNull Context context,
          double lat,
          double lng,
          boolean isDeparture
    )
    

Foreground Location

Pilgrim’s traditional visit detection works effortlessly in the background, allowing continued interaction with your users regardless of if they are in your app or not. But what about when you want to manually find a user’s current location while they are interacting within your app? Pilgrim allows you to actively request the current location manually when the app is in use by calling the PilgrimSdk.get().getCurrentLocation() method:

if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Result<CurrentLocation, Exception> currentLocationResult = PilgrimSdk.get().getCurrentLocation();
            if (currentLocationResult.isOk()) {
                CurrentLocation currentLocation = currentLocationResult.getResult();
                Log.d("PilgrimSdk", "Currently at " + currentLocation.getCurrentPlace().toString() + " and inside " + currentLocation.getMatchedGeofences().size() + " geofence(s)");
            } else {
                Log.e("PilgrimSdk", currentLocationResult.getErr().getMessage(), currentLocationResult.getErr());
            }
        }
    }).start();
}
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    Thread(Runnable {
        val currentLocationResult: com.foursquare.pilgrim.Result<CurrentLocation, Exception> = PilgrimSdk.get().currentLocation
        if (currentLocationResult.isOk) {
            val currentLocation: CurrentLocation = currentLocationResult.result
            Log.d("PilgrimSdk", "Currently at ${currentLocation?.currentPlace} and inside ${currentLocation?.matchedGeofences?.size} geofence(s)")
        } else {
            Log.e("PilgrimSdk", currentLocationResult.err!!.message, currentLocationResult.err)
        }
    }).start()
}

This will return the current venue the device is most likely at (in the currentLocation object), as well as any geofences that the device is in (if configured). Note that this will forgo any “stop detection” so it shouldn’t be used as a proxy for visits. For example, this method could return a high confidence result for Starbucks if called while someone is walking by the entrance.

Reasons for a nil or error response

There are a couple reasons that calling getCurrentLocation would return an error or a nil value:

  1. Lack of network connectivity: The most likely reason the SDK cannot retrieve the device’s current location would be because the API request has failed to make a proper connection to the Foursquare server.
  2. Lack of location permissions: If the proper location permissions have not been properly set or granted by the user.
  3. getCurrentLocation is being run from the main thread.

Tips

Targeting Android Marshmallow

If you are using targetSdk 23, then you will need to make sure that your app requests the ACCESS_FINE_LOCATION permission. Pilgrim will not run without the location permission. Once your user accepts the location permission, you should call PilgrimSdk.start(context).

public void requestLocationPermission(Activity activity, int requestCode) {
    ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            requestCode);
}

Next Steps

API Reference Docs

Implementation Checklist

Configuration

Was this page helpful?
Yes
No
Thank you!