Skip to main content
The AppDNA onboarding module lets you present server-driven onboarding flows that are configured in the AppDNA Console. Flows are delivered to the SDK via the remote config bundle, so you can update onboarding experiences without shipping app updates.

Present an Onboarding Flow

Present a specific onboarding flow by ID:
let presented = AppDNA.presentOnboarding(
    flowId: "main_flow",
    from: viewController,
    delegate: self
)

if !presented {
    print("Flow config not available — check Console or network")
}
The method returns false if the flow configuration is not available (e.g., config has not loaded yet or the flow ID is invalid).
If flowId is nil, the SDK presents the currently active flow as configured in remote config. This is useful when you want the Console to control which flow is shown.

Module Access

Access the onboarding module directly:
let onboarding = AppDNA.onboarding

Module Methods

MethodSignatureDescription
presentpresent(flowId: String?, from: UIViewController, context: OnboardingContext?) -> BoolPresent an onboarding flow
setDelegatesetDelegate(_ delegate: AppDNAOnboardingDelegate?)Set a delegate for flow callbacks

OnboardingContext

Pass additional context when presenting a flow:
let context = OnboardingContext(
    source: "app_launch",
    campaign: "winter_2025",
    referrer: "social_ad",
    userProperties: ["locale": "en_US"],
    experimentOverrides: ["onboarding_variant": "b"]
)

AppDNA.onboarding.present(
    flowId: "main_flow",
    from: viewController,
    context: context
)
PropertyTypeDescription
sourceString?Where the flow was triggered from
campaignString?Attribution campaign identifier
referrerString?Referral source
userProperties[String: Any]?Additional user properties for personalization
experimentOverrides[String: String]?Override experiment variant assignments for testing

AppDNAOnboardingDelegate

Implement the delegate protocol to respond to onboarding flow events:
protocol AppDNAOnboardingDelegate {
    func onOnboardingStarted(flowId: String)
    func onOnboardingStepChanged(flowId: String, stepId: String, stepIndex: Int, totalSteps: Int)
    func onOnboardingCompleted(flowId: String, responses: [String: Any])
    func onOnboardingDismissed(flowId: String, atStep: Int)
}

Example Implementation

class OnboardingHandler: AppDNAOnboardingDelegate {
    func onOnboardingStarted(flowId: String) {
        print("Onboarding started: \(flowId)")
    }

    func onOnboardingStepChanged(
        flowId: String,
        stepId: String,
        stepIndex: Int,
        totalSteps: Int
    ) {
        print("Step \(stepIndex + 1)/\(totalSteps): \(stepId)")
        // Update progress indicator
    }

    func onOnboardingCompleted(flowId: String, responses: [String: Any]) {
        print("Onboarding completed: \(flowId)")
        print("User responses: \(responses)")
        // Navigate to main app screen
        // Use responses to personalize the experience
    }

    func onOnboardingDismissed(flowId: String, atStep: Int) {
        print("Onboarding dismissed at step \(atStep)")
        // Handle early exit — maybe show again later
    }
}

Step Types

Onboarding flows support the following step types, configured in the Console:
Step TypeDescription
welcomeWelcome screen with title, subtitle, and hero image
questionSingle-select or multi-select question for user input
value_propValue proposition screen highlighting a key feature or benefit
customCustom HTML or native content rendered via a template
Step types and their content are defined entirely in the Console. The SDK renders them automatically based on the flow configuration. You do not need to build UI for individual step types.

Auto-Tracked Events

The SDK automatically tracks the following onboarding-related events:
EventTriggered When
onboarding_flow_startedAn onboarding flow begins
onboarding_step_viewedA step is displayed to the user
onboarding_step_completedA user completes a step (e.g., answers a question)
onboarding_step_skippedA user skips a step
onboarding_flow_completedThe user completes the entire flow
onboarding_flow_dismissedThe user dismisses the flow before completing
Each event includes the flowId, stepId, and stepIndex where applicable.

Configuration in Console

Onboarding flows are managed in the AppDNA Console:
  1. Navigate to Onboarding > Flows.
  2. Create a new flow or edit an existing one.
  3. Add steps (welcome, question, value_prop, custom) and configure their content.
  4. Set targeting rules to control which users see the flow.
  5. Publish the flow to make it available to the SDK via the config bundle.
Flows must be published in the Console before they appear in the SDK. Draft flows are not delivered to client devices.

Full Example

import AppDNASDK

class OnboardingCoordinator: AppDNAOnboardingDelegate {
    private let rootViewController: UIViewController

    init(rootViewController: UIViewController) {
        self.rootViewController = rootViewController
        AppDNA.onboarding.setDelegate(self)
    }

    func showOnboardingIfNeeded() {
        // Pass nil to show the active flow from remote config
        let presented = AppDNA.presentOnboarding(
            flowId: nil,
            from: rootViewController,
            delegate: self
        )

        if !presented {
            // No active flow or config not loaded yet
            navigateToMainApp()
        }
    }

    // MARK: - AppDNAOnboardingDelegate

    func onOnboardingStarted(flowId: String) {
        print("Starting flow: \(flowId)")
    }

    func onOnboardingStepChanged(
        flowId: String,
        stepId: String,
        stepIndex: Int,
        totalSteps: Int
    ) {
        // Track progress
    }

    func onOnboardingCompleted(flowId: String, responses: [String: Any]) {
        // Personalize based on responses
        if let goal = responses["fitness_goal"] as? String {
            AppDNA.identify(userId: currentUserId, traits: ["fitness_goal": goal])
        }
        navigateToMainApp()
    }

    func onOnboardingDismissed(flowId: String, atStep: Int) {
        navigateToMainApp()
    }
}