Userpilot iOS SDK enables you to capture user insights and deliver personalized in-app experiences in real time. With just a one-time setup, you can immediately begin leveraging Userpilot’s analytics and engagement features to understand user behaviors and guide their journeys in-app. This document provides a step-by-step walkthrough of the installation and initialization process, as well as instructions on using the SDK’s public APIs.

Prerequisites

Before you begin, ensure your iOS project meets the following requirements:
  • iOS Deployment Target: 13 or higher.
  • Xcode: Version 15 or higher.

Installation

Cocoapods

  1. Add the Userpilot dependency to your Podfile . Replace <SDK_VERSION> with the latest version available. You can fetch the latest version from here.
target 'YourTargetName' do
  pod 'Userpilot', '~><SDK_VERSION>'
end
  1. Run pod install in your project directory.

Swift Package Manager

  1. In Xcode, navigate to File -> Add Packages.
  2. Enter the package URL: https://github.com/Userpilot/ios-sdk.
  3. For Dependency Rule, select Up to Next Major Version.
  4. Click Add Package.
Once integrated, the Userpilot SDK is available throughout your application.

Initialization

To use Userpilot, initialize it once in your App Delegate or Scene Delegate during app launch. This ensures the SDK is ready as soon as your app starts. Replace <APP_TOKEN> with your Application Token, which can be fetched from your Environments Page.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var userpilot: Userpilot?

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        userpilot = Userpilot(config: Userpilot.Config(token: "APP_TOKEN")
						.logging(true) // Enable or disable logging
          			)  
		return true
    }
}

Using the SDK

Once initialized, the SDK provides straightforward APIs for identifying users, tracking events, and screen views.

Identifying Users (Required)

This API is required to identify unique users and companies (groups of users) and set their properties. Once identified, all subsequent tracked events and screens will be attributed to that user. Recommended Usage:
  • On user authentication (login): Immediately call identify when a user signs in to establish their identity for all future events.
  • On app launch for authenticated users: If the user has a valid authenticated session, call identify at app launch.
  • Upon property updates: Whenever user or company properties change.
userpilot.identify(
    userId: "<USER_ID>",
    userProperties: [
        "name": "John Doe",
        "email": "user@example.com",
        "created_at": "2019-10-17",
        "role": "Admin"
    ],
    company: [
        "id": "<COMPANY_ID>",
        "name": "Acme Labs",
        "created_at": "2019-10-17",
        "plan": "Free"
    ]
)
Properties Guidelines:
  • Key id is required in company properties to identify a unique company.
  • Userpilot supports String, Numeric, and Date types.
  • Send date values in ISO8601 format.
  • If you are planning to use Userpilot’s localization features, make sure you are passing user property locale_code with a value that adheres to ISO 639-1 format.
  • Use reserved property keys:
    • email for the user’s email.
    • name for the user’s or company’s name.
    • created_at for the user’s or company’s signup date.
Notes
  • Ensure the User ID source is consistent across Web, Android, and iOS.
  • While properties are optional, setting them enhances Userpilot’s segmentation capabilities.

Tracking Screens (Required)

Calling screen is crucial for unlocking Userpilot’s core engagement and analytics capabilities. When a user navigates to a particular screen, invoking screen records that view and triggers any eligible in-app experiences. Subsequent events are also attributed to the most recently tracked screen, providing context for richer analytical insights. For these reasons, we strongly recommend tracking all of your app’s screen views.
userpilot.screen("Profile")

Tracking Events

Log any meaningful action the user performs. Events can be button clicks, form submissions, or any custom activity you want to analyze. Optionally, pass metadata to provide context.
userpilot.track("Added to Cart", properties: ["itemId": "sku_456", "price": 29.99])

Logging Out

When a user logs out, call logout to clear the current user context. This ensures subsequent events are no longer associated with the previous user.
userpilot.logout()

Anonymous Users

If a user is not authenticated, call anonymous to track events without a user ID. This is useful for pre-signup flows or guest sessions.
userpilot.anonymous()
Anonymous users count towards your Monthly Active Users usage. Consider your MAU limits before using this API.

Experience

Triggers a specific experience programmatically using it’s ID. This API allows you to manually initiate an experience within your application.
userpilot.triggerExperience("<EXPERIENCE_ID>")
To end current active experience
userpilot.endExperience()

Configurations (Optional)

If you have additional configuration needs, you can pass a custom configuration when initializing Userpilot. You can enable logging, provide navigation and experience delegates, and set up analytics listeners.
let userpilot = Userpilot(
    config: Userpilot.Config(token: "APP_TOKEN")
        .logging(true) // Enable or disable logging
)
userpilot.navigationDelegate = self
userpilot.analyticsDelegate = self
userpilot.experienceDelegate = self
Defines how your app handles deep link routes triggered by Userpilot experiences. Implement this to route users to the appropriate screens or external URLs.
@objc
public protocol UserpilotNavigationDelegate: AnyObject {
    func navigate(to url: URL)
}
The Userpilot SDK automatically handles navigation if you haven’t implemented the UserpilotNavigationHandler . When a deep link is external, the SDK will handle it appropriately. For complete control over link handling, you can override the UserpilotNavigationHandler protocol. This allows you to customize the behavior for all types of links as per your requirements.

Analytics Listener

Receives callbacks whenever the SDK tracks an event, screen, or identifies a user. Implement this if you need to integrate with another analytics tool or log events for debugging.
@objc
public enum UserpilotAnalytic: Int {
    /// Represents an identify event, typically used to associate a user with a specific ID.
    case identify = 0

    /// Represents a screen tracking event, used to track visits to specific screens.
    case screen = 1

    /// Represents a custom event, used to track specific user interactions or actions.
    case event = 2

    /// Converts the analytic type to its string representation.
    public var rawValueString: String {
        switch self {
        case .identify:
            return "Ddentify"
        case .screen:
            return "Screen"
        case .event:
            return "Event"
        }
    }
}

/// A protocol that allows observation of analytics events emitted by the Userpilot SDK.
@objc
public protocol UserpilotAnalyticsDelegate: AnyObject {
    /// Notifies the delegate when a Userpilot analytics event is tracked.
    ///
    /// - Parameters:
    ///   - analytic: The type of analytic being tracked. This can be one of:
    ///     - `.identify`: Represents an identify event.
    ///     - `.screen`: Represents a screen tracking event.
    ///     - `.event`: Represents a custom event.
    ///   - value: The primary value associated with the analytic:
    ///     - For `.identify`: The user ID.
    ///     - For `.screen`: The screen title.
    ///     - For `.event`: The event name.
    ///   - properties: Optional dictionary containing additional context or metadata
    ///     about the analytic event.
    func didTrack(analytic: UserpilotAnalytic, value: String, properties: [String: Any]?)
}

Experience Listener

Receives callbacks when Userpilot experiences start, complete, or are dismissed, as well as changes in their step-by-step progression. Implement this if you want to pipe these data points to a destination or react to user actions.
/// The Userpilot experience type.
@objc
public enum UserpilotExperienceType: Int {
    case flow
    case survey
    case nps
}

/// The various states of a Userpilot experience.
@objc
public enum UserpilotExperienceState: Int {
    /// Indicates that the  experience/step has started.
    case started

    /// Indicates that the  experience/step has been completed successfully.
    case completed

    /// Indicates that the  experience/step was dismissed before completion.
    case dismissed

    /// Indicates that the  experience/step was skipped.
    case skipped

    /// Indicates that the experience/step was Submitted.
    case submitted
}

/// A protocol to observe and respond to state changes in Userpilot experiences.
@objc
public protocol UserpilotExperienceDelegate: AnyObject {

    /// Called when the state of a Userpilot experience changes.
    ///
    /// - Parameters:
    ///   - experienceType: The current experience type.
    ///   - experienceId: A unique identifier for the experience.
    ///   - experienceState: The current state of the experience.
    func onExperienceStateChanged(
        experienceType: UserpilotExperienceType,
        experienceId: NSNumber?, 
        experienceState: UserpilotExperienceState
    )

    /// Called when the state of a specific step within a Userpilot experience changes.
    ///
    /// - Parameters:
    ///   - experienceType: The current experience type.
    ///   - experienceId: A unique identifier for the experience.
    ///   - stepId: A unique identifier for the step.
    ///   - stepState: The current state of the step.
    ///   - step: The current step number in the experience.
    ///   - totalSteps: The total number of steps in the experience.
    func onExperienceStepStateChanged(
        experienceType: UserpilotExperienceType,
        experienceId: NSNumber,
        stepId: NSNumber,
        stepState: UserpilotExperienceState,
        step: NSNumber?,
        totalSteps: NSNumber?
    )
}

Push Notifications

Userpilot SDK supports handling push notifications to help you deliver targeted messages and enhance user engagement.

Prerequisites

It is recommended to configure your iOS push settings in the Userpilot Settings Studio before setting up push notifications in your app. To obtain the required keys and configuration details, please refer to the iOS Push Notification Guide.

Enabling Push Notification Capabilities

In Xcode, navigate to the Signing & Capabilities section of your main app target and add the Push Notifications capability.

Configuring Push Notifications

The Userpilot iOS SDK supports receiving push notification so you can reach your users whenever the moment is right. There are two options for configuring push notification: automatic or manual. Automatic configuration is the quickest and simplest way to configure push notifications and is recommended for most customers.

Automatic App Configuration

Automatic configuration takes advantage of swizzling to automatically provide the necessary implementations of the required UIApplicationDelegate and UNUserNotificationCenterDelegate methods. To enable automatic configuration, call Userpilot.enableAutomaticPushConfig() from UIApplicationDelegate.application(_:didFinishLaunchingWithOptions:) .
func application(
_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
   // Automatically configure for push notifications
   Userpilot.enableAutomaticPushConfig()

   // Override point for customization after application launch.
	return true
}
Automatic configuration seamlessly integrates with your app’s existing push notification handling. It processes only Userpilot notifications, while ensuring that all other notifications continue to be handled by your app’s original logic.

Manually Configuring Push Notifications

Step 1. Register for push notifications
// AppDelegate.swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
   application.registerForRemoteNotifications()

   // ...
}
Step 2. Set push token for Userpilot To enable push notifications, your app must explicitly request permission from the user.
private func requestPushNotificationPermission() {
        let options: UNAuthorizationOptions = [.alert, .sound, .badge]

        // Check if the app has already been authorized
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            if settings.authorizationStatus == .authorized {
                // App is already authorized for push notifications
                // Register for remote notifications again to get the device token
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            } else {
                // If not authorized, request permission
                UNUserNotificationCenter.current().requestAuthorization(options: options) { (granted, error) in
                    if granted {
                        DispatchQueue.main.async {
                            UIApplication.shared.registerForRemoteNotifications()
                        }
                    } else {
                        if let errorDescription = error?.localizedDescription {
                            print("Permission denied or failed to request: \(errorDescription)")
                        } else {
                            print("Permission denied or failed to request: Unknown error")
                        }
                    }
                }
            }
        }
    }
Call Userpilot.setPushToken(_:) from UIApplicationDelegate.application(_:didRegisterForRemoteNotificationsWithDeviceToken:) to pass the APNs token from calling registerForRemoteNotifications() to Userpilot.
// AppDelegate.swift

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
   userpilotInstance.setPushToken(deviceToken)
}
Step 3. Enable push response handling Update your AppDelegate to conform to the UNUserNotificationCenterDelegate protocol and assign self the delegate in application(_:didFinishLaunchingWithOptions:) . Implement userNotificationCenter(_:didReceive:withCompletionHandler:) and pass the received notification response to Userpilot.didReceiveNotification(response:completionHandler:) .
// AppDelegate.swift

@main
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
   func application(_ application: UIApplication, didFinishLaunchingWithOptions  launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
      application.registerForRemoteNotifications()
   UNUserNotificationCenter.current().delegate = self

   }

   func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive   response: UNNotificationResponse, withCompletionHandler completionHandler:   @escaping () -> Void) {
      if userpilotInstance.didReceiveNotification(response: response, completionHandler: completionHandler) {
      // NOTE: Userpilot calls the completion handler if the notification is an   Userpilot notification.
          return
      }
      completionHandler()
   }
}
Step 4. Configure foreground handling Configure handling of push notifications received while your app is in the foreground by implementing userNotificationCenter(_:willPresent:withCompletionHandler:) .
// AppDelegate.swift

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
   completionHandler([.banner, .list])
}
For more details refer to AppDelegate+PushNotification.swift file in sample app.

Sample

The Sample directory in GitHub repository contains a full example swift app providing references for usage of the Userpilot API.