iOS Quickstart

Get running with the iOS SDK in 5 minutes

Get the Convert iOS SDK running and serving your first experiment. This page is the fast path; for the conceptual background read Quickstart Overview and How Convert Works first.

You need an SDK Key from your Convert dashboard and an Xcode project targeting iOS 15+, macOS 12+, or tvOS 15+.

1. Add the dependency

Swift Package Manager (preferred)

In Xcode, choose File ▸ Add Package Dependencies…, enter the URL below, and add the ConvertSwiftSDK product to your app target:

https://github.com/convertcom/ios-sdk.git

Or add it to Package.swift:

// Package.swift
dependencies: [
    .package(url: "https://github.com/convertcom/ios-sdk.git", from: "1.0.0")
],
targets: [
    .target(name: "MyApp", dependencies: [
        .product(name: "ConvertSwiftSDK", package: "ios-sdk")
    ])
]

CocoaPods

# Podfile
pod 'ConvertSwiftSDK', '~> 1.0'

Then run pod install and open the generated .xcworkspace. ConvertSwiftSDK pulls ConvertSwiftSDKCore transitively — you only name ConvertSwiftSDK.

See Installation for toolchain requirements and the privacy manifest.

2. Initialize once at app startup

ConvertSwiftSDK owns a background URLSession, an offline event queue, and a config-refresh scheduler, so create exactly one instance per process.

SwiftUI

import SwiftUI
import ConvertSwiftSDK

@main
struct MyApp: App {
    let sdk = ConvertSwiftSDK(configuration: ConvertConfiguration(
        sdkKey: "YOUR_SDK_KEY"  // from your Convert dashboard
    ))

    var body: some Scene {
        WindowGroup {
            ContentView()
                .task {
                    try? await sdk.ready()
                }
                .environmentObject(sdk)  // or pass `sdk` down however your app shares singletons
        }
    }
}

UIKit AppDelegate

import UIKit
import ConvertSwiftSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var sdk: ConvertSwiftSDK!

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        sdk = ConvertSwiftSDK(configuration: ConvertConfiguration(
            sdkKey: "YOUR_SDK_KEY",  // from your Convert dashboard
            logLevel: .info          // use .debug while integrating
        ))
        return true
    }
}

3. Await config, then run an experience

The SDK fetches its bucketing config in the background. await sdk.ready() suspends until decisions are available; runExperience called before that returns nil.

// async/await (in a Task or async function)
try await sdk.ready()

let ctx = sdk.createContext()  // auto-persisted UUID visitor ID
if let variation = await ctx.runExperience("homepage-redesign") {
    switch variation.key {
    case "control":
        renderControl()
    case "treatment":
        renderTreatment()
    default:
        renderControl()
    }
} else {
    renderControl()  // nil = not ready / visitor not bucketed — not an error
}

Completion-handler style (UIKit / callback call sites)

sdk.ready { result in
    // Delivered on MainActor
    guard case .success = result else { return }
    let ctx = self.sdk.createContext()
    Task { @MainActor in
        if let variation = await ctx.runExperience("homepage-redesign") {
            self.applyVariation(variation.key)
        }
    }
}

4. Track a conversion

// Bare conversion (deduped per visitor + goal automatically).
await ctx.trackConversion("signup-completed")

// With revenue goal data.
await ctx.trackConversion(
    "purchase-completed",
    goalData: [.amount: .double(49.99), .transactionId: .string("tx-42")]
)

// Force a second transaction entry for a repeat purchase (conversion event stays deduped).
await ctx.trackConversion(
    "purchase-completed",
    goalData: [.amount: .double(29.99)],
    forceMultipleTransactions: true
)

trackConversion is async but never throws. Events are batched, persisted to disk, and flushed in the background — they survive the device going offline and an app restart. An unknown goal key logs a [WARN] and is silently dropped. See Offline Behavior.

Next steps