Skip to main content
Configure how your app handles navigation, analytics events, and experience lifecycle callbacks. 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 for external links if you haven’t implemented UserpilotNavigationDelegate. For complete control over link handling, implement UserpilotNavigationDelegate to customize behavior for all link types. Example Implementation
// in AppDelegate.h
#import <UIKit/UIKit.h>
@import Userpilot;

@interface AppDelegate : UIResponder <UIApplicationDelegate, UserpilotNavigationDelegate>
@end

// in AppDelegate.m
#pragma mark - UserpilotNavigationDelegate

- (void)navigateTo:(NSURL *)url {
    NSLog(@"Userpilot requested navigation to URL: %@", url.absoluteString);

    // Example: open the URL externally in Safari
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
    }

    // Or handle specific URLs in-app using conditionals
    // e.g., check url.host or pathComponents
}

Analytics Listener

Receives callbacks whenever the SDK tracks an event, screen, or identifies a user. Use this 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 "Identify"
        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]?)
}
Example Implementation
#pragma mark - UserpilotAnalyticsDelegate

- (void)didTrackWithAnalytic:(UserpilotAnalytic)analytic
                       value:(NSString *)value
                  properties:(NSDictionary<NSString *, id> *)properties {
    NSLog(@"Tracked event - type: %ld, value: %@, properties: %@", (long)analytic, value, properties);
}

Experience Listener

Receives callbacks when experiences start, complete, or are dismissed, and when step state changes. Implement this to forward data to another destination or to 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?
    )
}
Example Implementation
#pragma mark - UserpilotExperienceDelegate

- (void)onExperienceStateChangedWithExperienceType:(UserpilotExperienceType)experienceType
                                      experienceId:(NSNumber *)experienceId
                                   experienceState:(UserpilotExperienceState)experienceState {
    NSLog(@"Experience [%@] type: %ld changed state to: %ld",
          experienceId, (long)experienceType, (long)experienceState);
}

- (void)onExperienceStepStateChangedWithExperienceType:(UserpilotExperienceType)experienceType
                                          experienceId:(NSNumber *)experienceId
                                               stepId:(NSNumber *)stepId
                                            stepState:(UserpilotExperienceState)stepState
                                                 step:(NSNumber *)step
                                           totalSteps:(NSNumber *)totalSteps {
    NSLog(@"Step [%@] of experience [%@] (type: %ld) changed state to: %ld - step %ld/%ld",
          stepId, experienceId, (long)experienceType, (long)stepState,
          step.intValue, totalSteps.intValue);
}