Initialization
ConvertSwiftSDK(configuration:), the ready() gate, direct-data mode, completion-handler overloads
ConvertSwiftSDK is constructed with a plain initializer — there is no builder pattern. Create exactly one instance per process — ideally from AppDelegate.application(_:didFinishLaunchingWithOptions:) or your SwiftUI @main entry point — because the SDK owns a long-lived URLSession, an offline event queue, a background-session manager, and a config-refresh scheduler. Multiple instances waste resources and can produce duplicate tracking events.
The initializer
import ConvertSwiftSDK
let sdk = ConvertSwiftSDK(configuration: ConvertConfiguration(sdkKey: "YOUR_SDK_KEY"))ConvertSwiftSDK.init(configuration:) is non-throwing and non-blocking — it returns immediately. Config loading begins in a detached Task; validation and the live fetch surface through ready().
Every ConvertConfiguration field except sdkKey carries a JS-SDK-parity default. See Configuration Options for the full list.
Awaiting readiness — ready()
ready()Config fetch is asynchronous. Until the first config loads, runExperience returns nil and runFeature returns a disabled Feature. Gate your first decision call with await sdk.ready():
// async/await (preferred)
do {
try await sdk.ready()
// Config is available — safe to call runExperience / runFeature.
} catch {
// Only throws on an unrecoverable configuration error
// (empty SDK key, or empty/invalid direct-data).
// A transient network failure does NOT throw — the SDK resolves
// degraded using a cached config if one exists.
}ready() latches: once it resolves, subsequent calls return immediately. If a cache exists from a previous launch, ready() may resolve before the live fetch completes — the SDK is immediately usable offline.
Completion-handler overload
For UIKit-style call sites that prefer a callback, a completion-handler twin delivers the result on the MainActor:
sdk.ready { result in
// Called on MainActor
switch result {
case .success:
let ctx = self.sdk.createContext()
let variation = // await ctx.runExperience(...)
case .failure(let error):
print("SDK configuration error: \(error)")
}
}Direct-data mode (pre-fetched config)
If you already have a config blob — shipped with your app bundle, fetched by a separate layer, or a test fixture — skip the network fetch entirely by using the configData initializer:
let configData: Data = loadBakedConfig() // your own loader
let sdk = ConvertSwiftSDK(configData: configData)
try await sdk.ready() // validates the payload; throws if empty or invalidResolution rules:
init(configData:)used → the SDK validates the data and skips the initial network fetch entirely.init(configuration:)used (standard path) → the SDK loads from a disk cache first (if available), then refreshes from the live CDN.- Invalid or empty data / empty SDK key →
ready()throwsConvertError.invalidConfiguration(_:); every subsequent decision call degrades (returnsnil/ disabledFeature).
Subscribing to events
Beyond readiness, the SDK emits named events on its event bus. Subscribe with on(_:callback:), which returns an EventListenerToken:
import ConvertSwiftSDK
let token = await sdk.on(.bucketing) { payload in
// payload carries the bucketing result (experienceKey, variationKey, visitorId)
}
// Later — unsubscribe with the token (preferred):
await sdk.off(token)The SystemEvent cases are listed in Return Types & Models and explained in the shared Event System doc.
Creating a visitor context
A ConvertContext binds one visitor to the SDK. There are three ways to create one:
// 1) Auto-visitor — UUID persisted in the Keychain, stable across launches
// (best-effort stability across reinstalls; never IDFA/IDFV).
let ctx = sdk.createContext()
// 2) Explicit visitor — you own the ID (e.g. your login ID).
let ctx = sdk.createContext(visitorId: "visitor-42")
// 3) Explicit visitor + initial attributes.
let ctx = sdk.createContext(
visitorId: "visitor-42",
attributes: ["plan": "premium", "accountAgeDays": 120]
)createContext is synchronous and non-blocking — a context can be created before ready() resolves (decisions will degrade until config is available). The explicit-id overloads neither read nor write the auto-UUID, so you can hold an anonymous context and a signed-in context in the same session.
Visitor ID persistence: the auto-generated UUID is written to the Keychain with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, so it persists across app launches but never syncs to the user's other devices via iCloud Keychain. Continuity across an uninstall and reinstall is best-effort only — the Keychain item may survive on some iOS versions and backup configurations, but this is not guaranteed. See Visitor Context.
SwiftUI entry point pattern
import SwiftUI
import ConvertSwiftSDK
@main
struct MyApp: App {
let sdk = ConvertSwiftSDK(configuration: ConvertConfiguration(sdkKey: "YOUR_SDK_KEY"))
var body: some Scene {
WindowGroup {
ContentView()
.task {
try? await sdk.ready()
}
.environmentObject(sdk) // share the single SDK instance however your app prefers
}
}
}UIKit AppDelegate pattern
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",
logLevel: .info
))
sdk.ready { result in
// result delivered on MainActor
if case .failure(let error) = result {
print("Convert SDK configuration error: \(error)")
}
}
return true
}
// Forward background URLSession events to the SDK for durable delivery.
func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void
) {
sdk.handleEventsForBackgroundURLSession(
identifier: identifier,
completionHandler: completionHandler
)
}
}The handleEventsForBackgroundURLSession forwarding is optional — it lets the OS release the background relaunch sooner once the SDK's background URLSession events are acknowledged. Events are never lost if you omit it; only the prompt-wake-completion optimization is skipped.
Next steps
- Configuration Options — every
ConvertConfigurationparameter and its default - Return Types & Models — what
runExperience/runFeaturereturn - Code Examples — end-to-end Swift snippets for every method