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
| Method | Signature | Description |
|---|
present | present(flowId: String?, from: UIViewController, context: OnboardingContext?) -> Bool | Present an onboarding flow |
setDelegate | setDelegate(_ 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
)
| Property | Type | Description |
|---|
source | String? | Where the flow was triggered from |
campaign | String? | Attribution campaign identifier |
referrer | String? | 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 Type | Description |
|---|
welcome | Welcome screen with title, subtitle, and hero image |
question | Single-select or multi-select question for user input |
value_prop | Value proposition screen highlighting a key feature or benefit |
custom | Custom 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:
| Event | Triggered When |
|---|
onboarding_flow_started | An onboarding flow begins |
onboarding_step_viewed | A step is displayed to the user |
onboarding_step_completed | A user completes a step (e.g., answers a question) |
onboarding_step_skipped | A user skips a step |
onboarding_flow_completed | The user completes the entire flow |
onboarding_flow_dismissed | The 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:
- Navigate to Onboarding > Flows.
- Create a new flow or edit an existing one.
- Add steps (welcome, question, value_prop, custom) and configure their content.
- Set targeting rules to control which users see the flow.
- 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()
}
}