Swift
Identify your users and track churn-signal events from iOS, macOS, tvOS, and watchOS apps. An actor-based client with an ordered durable queue, retry handling, and stable event idempotency.
Install
Section titled “Install”Add the package in Xcode (File → Add Package Dependencies…) or in
Package.swift:
.package(url: "https://github.com/WhisperrAI/whisperr-swift.git", from: "0.1.0")Published on the Swift Package Index as WhisperrAI/whisperr-swift.
Initialize
Section titled “Initialize”Call once at startup. Get an app ingestion key from the Whisperr dashboard → Developer → API Keys.
import Whisperr
await Whisperr.initialize(apiKey: "wrk_xxx")Or construct a client directly for explicit lifetimes and dependency injection:
let whisperr = WhisperrClient(apiKey: "wrk_xxx")Identify
Section titled “Identify”Set who the current user is. Idempotent and safe to call on every login. Traits are merged server-side; channels are how Whisperr can reach the user — and whether it’s allowed to.
// Common case — email/phone/pushToken expand into opted-in channels:try await whisperr.identify( "user_123", traits: ["name": "Ada", "plan": "pro"], email: "ada@example.com", phone: "+15551234567")
// Full control — consent and verification:try await whisperr.identify( "user_123", channels: [ .email("ada@example.com", verified: true), .sms("+15551234567", optedIn: false) // opted out of SMS ])Push tokens
Section titled “Push tokens”The SDK never bundles a push library — hand it the APNs device token (or an
FCM registration token string) and Whisperr keeps the push channel current.
setPushToken is safe to call on every launch: repeated tokens are a no-op, a
rotated token opts the previous one out, and a token set before login buffers
and attaches to the next identify().
func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Task { // Hex-encodes the token and forwards it to setPushToken(_:). try await whisperr.setPushToken(deviceToken: deviceToken) }}After reset() (logout), call setPushToken again once the next user logs in.
Record product events. Buffered and sent in batches; the timestamp is captured at call time, so events recorded offline keep their real time.
try await whisperr.track("checkout_completed", properties: ["amount": 42, "currency": "USD"])track() uses the user from the most recent identify() — it throws
WhisperrClientError.missingUserID if no user is known. Pass userID:
explicitly for events emitted outside a signed-in session:
try await whisperr.track("trial_expired", userID: "user_123")Event names must be snake_case — see Event design.
Logout
Section titled “Logout”await whisperr.reset() // flushes, then clears the current userHow delivery works
Section titled “How delivery works”- Durable queue —
identifyandtrackappend to an ordered queue and deliver in order;trackcalls coalesce into/v1/events/batch. - Offline — the queue is persisted (via
UserDefaultsby default) and survives app restarts. Transient failures retry with exponential backoff, auth errors pause delivery and keep the queue, permanent client errors drop the offending item — the standard delivery contract. - Each event carries a stable
$message_id, so at-least-once retries dedup server-side.
Options
Section titled “Options”let whisperr = WhisperrClient( apiKey: "wrk_xxx", options: WhisperrOptions( flushInterval: 15, // seconds flushAt: 20, maxBatchSize: 500, // backend hard cap maxQueueSize: 1_000, // drops oldest beyond this enablePersistence: true, debug: false, onError: { error in print("whisperr:", error.type.rawValue, error.message) } ))
await whisperr.flush() // force delivery (e.g. before a critical await)Pass InMemoryWhisperrPersistence() for tests or ephemeral runtimes, or your
own WhisperrPersistence for custom storage.
A note on the API key
Section titled “A note on the API key”The ingestion key is embedded in your app, like a Segment write key or Amplitude API key. It can only ingest events for your app; treat it as publishable, not secret.