Objective-C Interop
Using the SDK from Objective-C / UIKit via completion-handler overloads
The Convert iOS SDK is Swift-first and async/await-native. Objective-C callers and UIKit-style completion-handler call sites are fully supported through two extension files that provide callback overloads of every async method. This page shows the callback forms; the Swift async forms are in Code Examples.
There is no Java on Apple platforms — the iOS equivalent of cross-language interop is Objective-C, covered below.
Swift 6 and Sendable
The SDK is compiled with .swiftLanguageMode(.v6) — strict concurrency is on. ConvertSwiftSDK is a Sendable final class (all stored properties are immutable lets of Sendable types); ConvertContext is likewise Sendable with no @unchecked suppression. Both types cross actor boundaries safely.
Completion callbacks are delivered on @MainActor so UIKit updates can happen directly inside the closure without a DispatchQueue.main.async wrapper.
Initialize the SDK
The SDK uses plain initializers, not a builder chain:
import ConvertSwiftSDK
// Swift async
let sdk = ConvertSwiftSDK(configuration: ConvertConfiguration(sdkKey: "YOUR_SDK_KEY"))
// With direct-data config (no network fetch)
let sdk = ConvertSwiftSDK(configData: myConfigData)Objective-C callers use the same initializers — they are bridged automatically because ConvertSwiftSDK is @objc-compatible via its Sendable final class shape.
Wait for readiness
The async ready() method has a completion-handler overload that delivers on @MainActor:
// Swift async
try await sdk.ready()
// Completion-handler (UIKit / Objective-C style)
sdk.ready { result in
// called on @MainActor
switch result {
case .success:
let context = sdk.createContext()
// ...
case .failure(let error):
print("SDK error:", error)
}
}Result<Void, ConvertError> bridges cleanly to Objective-C as a two-parameter callback (success Bool + nullable error) when called from .m files.
Create a context and run experiences
createContext(visitorId:attributes:) is synchronous and non-throwing — call it directly:
let context = sdk.createContext()
let knownContext = sdk.createContext(visitorId: "user-42", attributes: ["plan": "pro"])runExperience and runExperiences have completion-handler overloads defined in ConvertContext+Completion.swift:
// Swift async
let variation = await context.runExperience("homepage-redesign")
// Completion-handler
context.runExperiences(enableTracking: true) { variations in
// called on @MainActor
for v in variations { applyVariation(v) }
}Note: There is no completion-handler overload for the single
runExperience(_:enableTracking:)call.ConvertContext+Completion.swiftships exactly one experience overload:runExperiences(enableTracking:completion:). For single-experience call sites in non-async contexts, useTask { let v = await context.runExperience("key") }or callrunExperiences(completion:)and filter the result.
Run features
// Swift async
let feature = await context.runFeature("checkout-v2")
if feature.status == .enabled {
// show the new checkout
}
// There is no completion-handler overload for runFeature or runFeatures —
// ConvertContext+Completion.swift does not include these overloads.
// From a non-async context, use Task:
Task {
let feature = await context.runFeature("checkout-v2")
await MainActor.run { applyFeature(feature) }
}Track a conversion
trackConversion is async; call it from a Task for UIKit callers:
// Swift async
await context.trackConversion("purchase-goal")
await context.trackConversion(
"purchase-goal",
goalData: GoalData(amount: 49.99, transactionId: "tx-42"),
forceMultipleTransactions: false
)
// From a UIKit action handler (non-async context)
Task {
await context.trackConversion("purchase-goal")
}Toggle tracking at runtime
The runtime tracking toggle has completion-handler overloads in ConvertSwiftSDK+Completion.swift:
// Swift async
await sdk.setTrackingEnabled(false)
let isOn = await sdk.isTrackingEnabled()
// Completion-handler forms (ConvertSwiftSDK+Completion.swift)
sdk.setTrackingEnabled(false) {
// called on @MainActor once the actor flag is closed
}
sdk.isTrackingEnabled { isOn in
print("tracking:", isOn)
}Subscribe to events
The event bus (sdk.on(_:callback:) / sdk.off(_:)) is async:
// Swift async
let token = await sdk.on(.ready) { _ in
print("SDK is ready")
}
// later:
await sdk.off(token)
// From a UIKit context (non-async)
Task {
let token = await sdk.on(.bucketing) { payload in
// payload is @Sendable — safe to use across actors
print("bucketed:", payload)
}
}The callback closure is @escaping @Sendable — it may be called from any actor/thread, so keep it free of non-Sendable captures.
Calling async SDK methods from Objective-C (.m files)
.m files)Swift async methods are not directly callable from Objective-C. The recommended pattern for .m files is to write a Swift bridging wrapper that calls the SDK from Task { }:
// BridgingHelper.swift
import ConvertSwiftSDK
@objc class ConvertBridge: NSObject {
private let sdk: ConvertSwiftSDK
@objc init(sdkKey: String) {
sdk = ConvertSwiftSDK(configuration: ConvertConfiguration(sdkKey: sdkKey))
}
@objc func ready(completion: @escaping (NSError?) -> Void) {
sdk.ready { result in
switch result {
case .success: completion(nil)
case .failure(let err): completion(err as NSError)
}
}
}
@objc func setTrackingEnabled(_ enabled: Bool, completion: @escaping () -> Void) {
sdk.setTrackingEnabled(enabled, completion: completion)
}
}Use @objc on the wrapper class and any method you need to call from .m files.
Quick reference
| Swift async | Callback / UIKit pattern |
|---|---|
ConvertSwiftSDK(configuration:) | Same — plain initializer, no builder |
try await sdk.ready() | sdk.ready { result in … } (on @MainActor) |
sdk.createContext() | Same — synchronous, call directly |
await context.runExperiences(enableTracking:) | context.runExperiences(enableTracking:) { variations in … } (on @MainActor) |
await context.trackConversion(_:goalData:) | Task { await context.trackConversion(…) } |
await sdk.setTrackingEnabled(_:) | sdk.setTrackingEnabled(_:) { } (on @MainActor) |
await sdk.isTrackingEnabled() | sdk.isTrackingEnabled { isOn in … } (on @MainActor) |
await sdk.on(_:callback:) | Task { let token = await sdk.on(…) { … } } |
Related pages
- Code Examples — the full Swift async forms
- Return Types & Models — the types referenced here
- Tracking Control — the runtime tracking toggle in depth