Swift

Native Swift SDK.

Overview

The Swift SDK provides client-side feature flag evaluation, identity lifecycle APIs, telemetry batching, stream updates with polling fallback, and state subscriptions.

Get started

Install with SwiftPM

SSWIFT
dependencies: [
.package(url: "https://github.com/Truflag/truflag-swift-sdk.git", from: "0.2.2")
]

Install with CocoaPods

RRUBY
pod 'TruflagSDK', '~> 0.2'

Configure

apiKey is required. If no user is supplied, the client starts with an anonymous identity. configure() awaits the initial refresh before returning.

SSWIFT
import TruflagSDK
let client = TruflagClient()
try await client.configure(
TruflagConfigureOptions(
apiKey: "env_c_...",
user: TruflagUser(
id: "user-123",
attributes: [
"plan": AnyCodable("pro"),
"country": AnyCodable("US"),
]
)
)
)
// configure() awaits initial refresh before returning
let ready = await client.isReady()

Configuration options

SSWIFT
TruflagConfigureOptions(
apiKey: "env_c_...",
user: TruflagUser(id: "user-123"),
baseURL: URL(string: "https://sdk.truflag.com")!,
streamURL: URL(string: "wss://stream.sdk.truflag.com")!,
streamEnabled: true,
pollingIntervalMs: 60_000,
requestTimeoutMs: 6_000,
cacheTtlMs: 300_000,
telemetryFlushIntervalMs: 10_000,
telemetryBatchSize: 50,
telemetryEnabled: true,
debugLoggingEnabled: false
)

Identity

SSWIFT
try await client.login(
user: TruflagUser(
id: "user-456",
attributes: ["plan": AnyCodable("enterprise")]
)
)
try await client.setAttributes([
"region": AnyCodable("us-east-1"),
"cohort": AnyCodable("A"),
])
try await client.logout() // switches to anonymous identity and refreshes

Flags and exposure

getFlag() and getAllFlags() automatically enqueue exposure events for returned flags. getFlagPayload() does not emit exposure by itself.

SSWIFT
let enabled: Bool = await client.getFlag("new-checkout", defaultValue: false)
let variant: String = await client.getFlag("checkout-variant", defaultValue: "control")
let payload = await client.getFlagPayload("new-checkout")
let allFlags = await client.getAllFlags()
// Optional manual exposure signal for state-driven reads:
await client.notifyFlagRead(flagKey: "new-checkout")

Events

Use track() for custom events. Set immediate: true to flush now.

SSWIFT
try await client.track(
eventName: "checkout_completed",
properties: [
"source": AnyCodable("swift"),
"value": AnyCodable(99.95),
],
immediate: true // flush now instead of waiting for batch/timer
)
try await client.expose(flagKey: "new-checkout")

Reactive updates

Subscribe to global state, per-flag updates, or consume async streams.

SSWIFT
let token = await client.subscribe {
Task {
let state = await client.getState()
print("ready:", state.ready, "flags:", state.flags.count)
}
}
let flagToken = await client.subscribeFlag("new-checkout") { flag in
print("flag changed:", flag?.value.value ?? "nil")
}
// Async stream alternatives:
let stateStream = await client.stateStream()
let flagStream = await client.flagStream("new-checkout")
// Later:
await client.unsubscribe(token)
await client.unsubscribeFlag(flagToken)

Troubleshooting

Flags return fallback values

Confirm await client.isReady() is true, yourapiKey targets the expected environment, and the flag key exists.

Stream updates are not arriving

Verify streamEnabled and streamURL. Polling fallback remains available.