Getting Started
Using AI to Code? Copy This Prompt
Paste this prompt into ChatGPT, Claude, Cursor, or any AI coding assistant to automatically integrate the Sequence SDK into your app.
Overview
Sequence lets you build beautiful onboarding screens in our visual editor, then show them natively in your iOS app. No design skills needed.
Installation
Open Xcode Package Manager
File β Add Packages
Paste this URL:
https://github.com/Musgrav/sequence-swiftImportant: Use Branch, Not Version
main. Do not use a version number.Quick Start
How It Works
Get Your API Credentials
From your Sequence dashboard
Go to Settings in your dashboard to find:
appIdapiKeyInitialize the SDK
Add to your App's init()
import Sequence
@main
struct MyApp: App {
init() {
Sequence.shared.configure(
appId: "your-app-id",
apiKey: "your-api-key",
baseURL: "https://screensequence.com"
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}Show the Onboarding
Add WebViewOnboardingView to your app
import SwiftUI
import Sequence
struct ContentView: View {
@StateObject private var sequence = Sequence.shared
var body: some View {
Group {
if sequence.isOnboardingCompleted {
// Show your paywall or main app
MainAppView()
} else {
// Sequence handles everything with pixel-perfect rendering
WebViewOnboardingView {
print("Onboarding finished!")
}
}
}
}
}Use WebViewOnboardingView
WebViewOnboardingView (not OnboardingView) for pixel-perfect WYSIWYG rendering that matches the editor exactly.Create Your Flow in the Dashboard
Design your screens visually
Go to Flows in your dashboard, add screens, drag in blocks, and hit Publish. Your app will automatically show the new onboarding.
You're all set!
Your app will now fetch and display your onboarding flow. Changes you make in the dashboard appear instantlyβno app update needed.
Core Concepts
Architecture
Sequence follows a decoupled architecture where your onboarding configuration lives on our servers and is fetched by the SDK at runtime:
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β Your Mobile β β Sequence β β Sequence β
β App ββββββΆβ API βββββββ Dashboard β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β
β 1. Fetch config β
βββββββββββββββββββββββββ
β β
β 2. Render screens β
β 3. Collect data β
β 4. Track events β
ββββββββββββββββββββββββΆβ
β βKey benefits of this architecture:
- Instant updates: Change your onboarding without releasing a new app version
- A/B testing: The API serves different variants to different users
- Analytics: All events flow back to the dashboard for analysis
- Offline support: Configurations are cached locally for reliability
Screens & Flows
A Flow is a collection of Screens that users navigate through. Each screen has a type that determines its purpose:
| Screen Type | Purpose | Common Use Cases |
|---|---|---|
welcome | First impression screen | App intro, value proposition |
feature | Highlight a feature | Feature tours, benefits |
permission | Request permissions | Notifications, location, camera |
carousel | Multi-slide content | Feature showcase, testimonials |
celebration | Success/completion | Signup complete, achievement |
native | Custom native screen | Login, complex forms |
filler | Transitional content | Loading states, progress |
Screen Transitions
Configure how screens animate when navigating:
// Available transitions
type ScreenTransition =
| 'none' // No animation
| 'fade' // Opacity fade
| 'slide-left' // Slide from right to left
| 'slide-right' // Slide from left to right
| 'slide-up' // Slide from bottom
| 'slide-down' // Slide from top
| 'fade-slide-left' // Fade + slide left
| 'fade-slide-right' // Fade + slide right
| 'scale'; // Scale up from centerContent Blocks
Screens are composed of Content Blocks β reusable UI components that you arrange to build your screens. Sequence provides 11 block types:
| Block | Description | Data Collection |
|---|---|---|
text | Headings, body text, captions | No |
image | Images with various fit modes | No |
button | Interactive buttons with presets | No |
input | Text fields (email, phone, password, etc.) | Yes |
checklist | Single/multi-select options | Yes |
slider | Numeric range selection | Yes |
progress | Progress indicators (bar, dots, ring) | No |
spacer | Vertical spacing | No |
divider | Horizontal line separator | No |
icon | Icons and emojis | No |
feature-card | Card with headline and body | No |
Block Styling
Data Collection
Input blocks (input, checklist, slider) collect user data during onboarding. Each block has a unique id that becomes the key in the collected data object:
// When onboarding completes, you receive:
{
"user_name": "John Doe", // From input block with id="user_name"
"interests": ["fitness", "music"], // From checklist block with id="interests"
"experience_level": 3, // From slider block with id="experience_level"
}
// Handle in your onComplete callback:
<OnboardingModal
onComplete={(collectedData) => {
// Save to your backend
saveUserPreferences(collectedData);
// Or use locally
if (collectedData.interests?.includes('fitness')) {
showFitnessContent();
}
}}
/>Validation
Configure validation rules to ensure users provide required information:
- Input blocks: Mark as required, set input types for format validation
- Checklist blocks: Set minimum/maximum selections
- Navigation: Users cannot proceed until validation passes
Migration Guide
This guide walks you through replacing your existing onboarding implementation with Sequence. Whether you have a simple tutorial or a complex multi-screen flow, we'll help you migrate smoothly.
Before You Start
Audit Your Current Onboarding
Document your existing onboarding to plan the migration:
- How many screens do you have?
- What data do you collect from users?
- What actions happen on each screen (permissions, API calls)?
- How do you track completion status?
- Do you have any A/B tests running?
Prepare Your Sequence Dashboard
- Create your app in the Sequence dashboard
- Copy your appId and apiKey from Settings
- Create a new Flow that mirrors your existing screens
Feature Parity
Step-by-Step Migration
Step 1: Install the SDK
Add Sequence alongside your existing onboarding (don't remove anything yet):
In Xcode, go to File β Add Packages and enter:
https://github.com/Musgrav/sequence-swiftStep 2: Initialize the SDK
Configure Sequence without changing existing functionality:
import Sequence
@main
struct MyApp: App {
init() {
Sequence.shared.configure(
appId: "your-app-id",
apiKey: "your-api-key"
)
}
var body: some Scene {
WindowGroup {
// Your existing app code - unchanged
ExistingAppWithOldOnboarding()
}
}
}Step 3: Create a Feature Flag
Use a feature flag to gradually roll out the new onboarding:
import Foundation
enum OnboardingType: String {
case legacy
case sequence
}
class OnboardingTypeManager: ObservableObject {
@Published var type: OnboardingType = .legacy
init() {
checkOnboardingType()
}
private func checkOnboardingType() {
// Check if user should see new onboarding
// Options: remote config, random assignment, user cohort, etc.
if let assigned = UserDefaults.standard.string(forKey: "onboarding_type"),
let onboardingType = OnboardingType(rawValue: assigned) {
self.type = onboardingType
return
}
// Random 50/50 assignment for new users
let newType: OnboardingType = Bool.random() ? .sequence : .legacy
UserDefaults.standard.set(newType.rawValue, forKey: "onboarding_type")
self.type = newType
}
}Step 4: Implement Side-by-Side
Show either the old or new onboarding based on the flag:
import SwiftUI
import Sequence
struct OnboardingGate<Content: View>: View {
@StateObject private var typeManager = OnboardingTypeManager()
@StateObject private var sequence = Sequence.shared
@State private var showLegacy = true
let content: () -> Content
var body: some View {
Group {
if typeManager.type == .legacy && showLegacy {
LegacyOnboarding { data in
handleOnboardingData(data)
showLegacy = false
}
} else if typeManager.type == .sequence && !sequence.isOnboardingCompleted {
WebViewOnboardingView {
// Onboarding completed
}
} else {
content()
}
}
}
private func handleOnboardingData(_ data: [String: Any]) {
// Your existing data handling logic
// Works the same for both onboarding types
saveUserPreferences(data)
trackOnboardingComplete(data)
}
}Step 5: Handle Custom Actions
If your onboarding has custom screens (login, permissions), use the delegate pattern:
// Set up the delegate to handle custom actions
Sequence.shared.delegate = self
// Implement the delegate methods
extension YourViewController: SequenceDelegate {
func sequence(_ sequence: Sequence, didRequestNativeScreen screenId: String, data: [String: Any]) -> Bool {
if screenId == "login-screen" {
// Show your existing login UI
presentLoginScreen {
// Continue onboarding after login
Sequence.shared.track(.screenCompleted, screenId: screenId)
}
return true // Indicates you're handling this screen
}
return false
}
func sequence(_ sequence: Sequence, didTriggerCustomAction action: String, screenId: String) {
switch action {
case "request-notifications":
requestNotificationPermission()
case "connect-social":
showSocialConnect()
default:
break
}
}
}Step 6: Monitor and Iterate
Compare metrics between old and new onboarding:
- Completion rates (visible in Sequence Analytics)
- Time to complete
- Drop-off points
- User feedback
Step 7: Remove Legacy Code
Once you're confident in the new onboarding, remove the feature flag and legacy implementation:
// Final clean implementation
@main
struct MyApp: App {
init() {
Sequence.shared.configure(
appId: "your-app-id",
apiKey: "your-api-key"
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@StateObject private var sequence = Sequence.shared
var body: some View {
Group {
if sequence.isOnboardingCompleted {
MainAppView()
} else {
WebViewOnboardingView {
// Onboarding completed
}
}
}
}
}Data Mapping
Map your existing data collection to Sequence block IDs:
| Your Current Field | Sequence Block Type | Block ID |
|---|---|---|
| Name input | input | user_name |
| Email input | [object Object] (email type) | user_email |
| Interest selection | checklist | interests |
| Experience slider | slider | experience_level |
Keep Block IDs Consistent
Testing Your Migration
Pre-Launch Checklist
- All screens render correctly on iOS and Android
- Navigation between screens works as expected
- All data is collected and passed to onComplete
- Custom actions (permissions, login) function properly
- Analytics events are appearing in the dashboard
- Offline behavior works (cached config loads)
- Completion state persists across app restarts
Testing Commands
import Sequence
// Clear completion status to re-show onboarding
Sequence.shared.reset()
// Or reset just the onboarding state
Sequence.shared.resetOnboarding()SDK Reference
Complete API reference for the Sequence Swift SDK. All classes and methods are available from the Sequence module.
import Sequence
// Core singleton
Sequence.shared
// Configuration
Sequence.shared.configure(appId: String, apiKey: String, baseURL: String?)
// User identification
Sequence.shared.identify(userId: String)
Sequence.shared.reset()
// Event tracking
Sequence.shared.track(_ eventType: EventType, screenId: String?, properties: [String: Any]?)
// Onboarding state
Sequence.shared.isOnboardingCompleted
Sequence.shared.resetOnboarding()Sequence Client
The Sequence.shared singleton manages SDK state, API communication, and event tracking. Initialize it in your app's entry point.
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
configure() | appId: String, apiKey: String, baseURL: String? | Void | Initialize the SDK with your credentials |
fetchConfig() | none | async throws -> OnboardingConfig | Fetch onboarding configuration from API |
identify() | userId: String | Void | Associate events with a user ID |
reset() | none | Void | Clear user data and completion status |
track() | _ eventType: EventType, screenId: String?, properties: [String: Any]? | Void | Track a custom event |
Usage Examples
import Sequence
class AuthManager {
// Identify user after login
func handleLogin(userId: String) {
Sequence.shared.identify(userId: userId)
}
// Track custom event
func handleButtonPress() {
Sequence.shared.track(.buttonTapped, screenId: "welcome-screen", properties: [
"button_label": "Get Started"
])
}
// Reset on logout
func handleLogout() {
Sequence.shared.reset()
}
}Configuration
Configure the Sequence SDK in your app's entry point. The configuration must be called before using any other SDK methods.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
appId | String | Yes | Your app ID from the dashboard |
apiKey | String | Yes | Your API key (keep secret) |
baseURL | String? | No | Custom API URL (optional) |
Configuration Example
import Sequence
// Configure in your App init
Sequence.shared.configure(
appId: "your-app-id",
apiKey: "your-api-key",
baseURL: "https://screensequence.com"
)Properties
Observable Properties
Sequence.shared is an ObservableObject, so you can use @StateObject or @ObservedObject to observe changes.
import SwiftUI
import Sequence
struct MyView: View {
@StateObject private var sequence = Sequence.shared
var body: some View {
VStack {
// Observe configuration state
if sequence.isConfigured {
Text("SDK is configured")
}
// Observe loading state
if sequence.isLoading {
ProgressView()
}
// Observe onboarding completion
if sequence.isOnboardingCompleted {
MainAppView()
} else {
OnboardingView { }
}
// Access screens
Text("\(sequence.screens.count) screens in this flow")
// Access experiment info
if let experiment = sequence.experimentInfo {
Text("Variant: \(experiment.variantName)")
}
}
}
}Available Properties
| Property | Type | Description |
|---|---|---|
isConfigured | Bool | SDK has been initialized |
isLoading | Bool | Currently fetching config |
error | Error? | Last error encountered |
config | OnboardingConfig? | Current onboarding configuration |
screens | [Screen] | Array of onboarding screens |
isOnboardingCompleted | Bool | Whether onboarding is complete |
experimentInfo | ExperimentInfo? | A/B test variant information |
Views
WebViewOnboardingView (Recommended)
Full-screen SwiftUI view that renders your onboarding flow using a WebView for pixel-perfect WYSIWYG rendering. This is the recommended view to use for production apps.
| Parameter | Type | Required | Description |
|---|---|---|---|
onComplete | (() -> Void)? | No | Called when onboarding completes |
onDataCollected | (([String: Any]) -> Void)? | No | Called with collected user data |
Why WebViewOnboardingView?
import SwiftUI
import Sequence
struct ContentView: View {
@StateObject private var sequence = Sequence.shared
var body: some View {
Group {
if sequence.isOnboardingCompleted {
MainAppView()
} else {
WebViewOnboardingView(
onComplete: {
print("Onboarding completed!")
},
onDataCollected: { data in
print("User data:", data)
saveToBackend(data)
}
)
}
}
}
}OnboardingView (Legacy)
Native SwiftUI view that renders an approximation of your onboarding flow. Not recommended for production use as it may not match the editor exactly.
Avoid OnboardingView
Delegate Protocol
Implement SequenceDelegate to handle custom screens and actions.
import Sequence
class MyAppDelegate: SequenceDelegate {
// Handle custom native screens
func sequence(_ sequence: Sequence, didRequestNativeScreen screenId: String, data: [String: Any]) -> Bool {
if screenId == "auth-screen" {
showAuthFlow()
return true // We're handling this
}
return false // Let Sequence handle it
}
// Handle custom button actions
func sequence(_ sequence: Sequence, didTriggerCustomAction action: String, screenId: String) {
if action == "open-settings" {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
}
}
}
// Set the delegate
Sequence.shared.delegate = MyAppDelegate()Swift Types
Key types used in the Swift SDK:
// Onboarding configuration from API
struct OnboardingConfig: Codable {
let version: Int
let screens: [Screen]
let progressIndicator: FlowProgressIndicator?
let transitions: [ScreenTransitionConfig]?
let experiment: ExperimentInfo?
}
// Individual screen
struct Screen: Codable, Identifiable {
let id: String
let appId: String
let flowId: String?
let name: String
let type: ScreenType
let order: Int
let content: ScreenContent
let transition: ScreenTransition?
let createdAt: String
let updatedAt: String
}
enum ScreenType: String, Codable {
case welcome
case feature
case carousel
case permission
case celebration
case native
case filler
}
// Event types for tracking
enum EventType {
case screenViewed
case screenFirstViewed
case screenCompleted
case screenSkipped
case screenDroppedOff
case buttonTapped
case onboardingStarted
case onboardingCompleted
case experimentVariantAssigned
}enum BlockType: String, Codable {
case text
case image
case button
case input
case checklist
case slider
case progress
case spacer
case divider
case icon
case featureCard = "feature-card"
}
// Experiment info
struct ExperimentInfo: Codable {
let id: String
let variantId: String
let variantName: String
}Full Type Definitions