iOS SDK
The Kixo iOS SDK supports Swift 5.9+ and iOS 14+. Distributed via Swift Package Manager. Auto-tracks screens, taps, sessions, crashes, network requests, push notifications, and lifecycle events with a single Kixo.start call.
Installation
Swift Package Manager
In Xcode, go to File → Add Package Dependencies and enter:
https://github.com/kixo/kixo-ios-sdkConfigure
Initialise Kixo in your SwiftUI App struct orAppDelegate:
import Kixo
@main
struct MyApp: App {
init() {
Kixo.start(writeKey: "YOUR_PROJECT_ID")
}
var body: some Scene {
WindowGroup { ContentView() }
}
}Note
One line is enough — the SDK auto-detects your environment (Simulator / DEBUG → development; TestFlight → staging; App Store → production), picks the matching ingest host, and turns on every auto-tracker. Override individual flags with ConfigurationOptions(...) only when you need to.
Configuration options
Kixo.start(
writeKey: "YOUR_PROJECT_ID",
options: ConfigurationOptions(
autoTrackScreens: true,
autoTrackTaps: true,
autoTrackNetwork: true,
autoTrackCrashes: true,
autoTrackSessions: true,
autoTrackPush: true,
sessionTimeout: 30,
flushInterval: 30,
flushAt: 20,
maxBufferSize: 200,
// apiHost: nil → resolved from environment
// debug: nil → true in DEBUG, false otherwise
// environment: nil → auto-detected
)
)Note
Server-controlled config.Every per-tracker flag can also be flipped from your dashboard'sSettings → Data Collection page. Server values override local defaults; project preferences override admin globals.
Auto-tracked events
screen_view— UIKit view-controller appearances + SwiftUI navigationsession_start/session_endtap— button taps and gesture recogniserserror/crash— uncaught exceptions, signals, NSExceptionnetwork— HTTP request performance via URLSession swizzlepush_received/push_open/push_dismissed/push_silent/push_action— full push lifecyclepush_permission/push_token_invalidatedlifecycle— foreground / background / app-launch transitions
Custom events
Kixo.track("purchase_completed", properties: [
"product_id": "SKU-123",
"amount": 49.99,
"currency": "USD",
])Typed event helpers
Sugar over Kixo.track for the events Kixo recognises by name (purchase, signup,subscribe_start, trial_start,cancel, upgrade, activation,share, invite). Compile-time validation of property shape, single source of truth on key names — backend's standard-event detector matches verbatim.
Kixo.trackPurchase(
amount: 49.99,
currency: "USD",
productId: "pro_yearly"
)
Kixo.trackSubscriptionStart(
plan: "pro",
amount: 9.99,
currency: "USD",
interval: .month
)
Kixo.trackSignup(method: "google")
Kixo.trackTrialStart(plan: "pro", days: 14)
Kixo.trackCancel(plan: "pro", reason: "too_expensive")
Kixo.trackUpgrade(fromPlan: "free", toPlan: "pro")
Kixo.trackActivation(event: "first_post_published")
Kixo.trackShare(channel: "twitter", contentId: "post_123")
Kixo.trackInvite(channel: "email", recipientCount: 5)Identify users
Reserved standard property keys carry a $-prefix (Mixpanel convention) so they namespace away from your own custom traits and promote to the dashboard's profile columns. Use the typed StandardProperty enum or the raw $-prefixed string — see the Standard property catalog below for the full 37-key list.
Kixo.identify(userId: "user_123", traits: [
"$email": "jane@example.com", // identity
"$name": "Jane Doe", // identity
"$plan": "pro", // subscription pack
"$lifetime_orders": 12, // e-commerce pack
"signup_source": "twitter_ad" // custom trait
])Tag a user for segmentation
Use setUserProperty with a boolean value to attach a simple yes/no tag to the user. The tag persists across sessions and powers segments, email campaigns, and chat queries — no setup beyond the SDK call.
// Tag a user as subscribed — segments + campaigns can target this
Kixo.setUserProperty("subscribe", value: true)
// VIP membership
Kixo.setUserProperty("vip", value: true)
// String + numeric values work too
Kixo.setUserProperty("plan_tier", value: "enterprise")
Kixo.setUserProperty("lifetime_orders", value: 42)
// Bulk-set
Kixo.setUserProperties([
"subscribe": true,
"plan_tier": "enterprise",
])Properties persist in UserDefaults across launches and auto-attach to every outbound event. In chat say things like "send a welcome email to users where subscribe is true" — Kixo builds the segment and drafts the template for you. Cleared on Kixo.reset().
Standard property catalog
Reserved property keys carry a $prefix so they namespace away from your custom traits. Kixo's catalog covers 37 keys across 3 universal packs (identity, geo, lifecycle) and 5 B2B vertical packs (subscription, e-commerce, media, marketplace, loyalty). Set whichever apply to your product — the dashboard adapts and renders only the packs you populate.
Identity
Always relevant. Sets the profile header columns.
| Key | Type | Description |
|---|---|---|
$email | string | Primary email, often the merge key for identity stitching. |
$phone | string | E.164 phone number. |
$name | string | Full display name. |
$first_name | string | Given name. |
$last_name | string | Family name. |
$avatar_url | string | Full URL to the user's avatar image. |
Geo
Geographic context.
| Key | Type | Description |
|---|---|---|
$country | string | ISO 3166 country code. |
$city | string | City name. |
$region | string | State or province. |
$timezone | string | IANA zone like America/Los_Angeles. |
$language | string | IETF tag like en or ru-RU. |
$locale | string | Full locale identifier. |
Lifecycle
When did we see them.
| Key | Type | Description |
|---|---|---|
$created | ISO8601 | Signup or account creation time. |
$last_seen | ISO8601 | Last engagement time. |
Subscription
Set if your product has plans.
| Key | Type | Description |
|---|---|---|
$plan | string | Tier slug — free, pro, enterprise. |
$subscription_status | string | active / trial / cancelled / past_due. |
$trial_ends | ISO8601 | When the current trial expires. |
$mrr | number | Monthly recurring revenue in account currency. |
$subscription_started | ISO8601 | When the current subscription began. |
E-commerce
Set if you sell products.
| Key | Type | Description |
|---|---|---|
$lifetime_orders | number | Count of completed orders. |
$lifetime_revenue | number | Total spend. |
$aov | number | Average order value. |
$last_purchase | ISO8601 | Most recent successful purchase. |
$first_purchase | ISO8601 | First successful purchase. |
$cart_abandoned_count | number | Lifetime count of cart abandonments. |
Media
Set if you publish content.
| Key | Type | Description |
|---|---|---|
$content_tier | string | free / premium / paid. |
$subscribed_categories | CSV string or array | Categories the user follows. |
$watch_time_total | number | Lifetime watch time in seconds. |
$last_played | ISO8601 | Most recent playback start. |
Marketplace
Set if you're a two-sided platform.
| Key | Type | Description |
|---|---|---|
$seller_tier | string | Seller-side tier slug. |
$buyer_tier | string | Buyer-side tier slug. |
$listings_count | number | Active listings the user owns. |
$reviews_count | number | Reviews the user has received. |
$verified | boolean | KYC status. |
Loyalty
Set for engagement and rewards programs.
| Key | Type | Description |
|---|---|---|
$loyalty_points | number | Current redeemable points balance. |
$vip_level | string | VIP tier slug. |
$referral_count | number | Successful referrals attributed to this user. |
Tip
Don't see your pattern? Use bare keys for custom traits. They surface in the dashboard's Custom Traits panel without polluting the profile columns. The 5 vertical packs above are opinionated guesses at the most common B2B shapes — customer-specific terminology (e.g. shipping_plan) stays bare.
Super-properties
Per-session key/value pairs auto-attached to every outbound event. Different from identify traits (which describe identity); super-properties describe session context — active A/B variant, build flavour, opted-in feature flags. Persisted in UserDefaults across launches; cleared on reset(). Per-event properties on track always win on collision.
Kixo.setSuperProperty("build_flavour", value: "beta")
Kixo.setSuperProperties([
"ab_variant": "B",
"referrer_campaign": "autumn-launch",
])
// Sugar for A/B tracking — keys as 'experiment_<id>'.
Kixo.setExperimentVariant("checkout_v2", variant: "variant_a")
Kixo.unsetSuperProperty("build_flavour")
Kixo.clearSuperProperties()SwiftUI screen tracking
SwiftUI screen views are auto-tracked when the SDK can resolve a view name. For finer control or custom names use the.kixoScreen() view modifier:
struct HomeView: View {
var body: some View {
VStack { Text("Welcome") }
.kixoScreen("HomeView")
}
}Push notifications
The SDK installs a runtime AppDelegate proxy on Kixo.start — silent pushes (content-available: 1) and background-delivered visible pushes are captured automatically. No code in your AppDelegate is required. ExistingUNUserNotificationCenterDelegate implementations continue to fire normally; Kixo wraps them.
Register the device token via the standarddidRegisterForRemoteNotificationsWithDeviceToken:
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let token = deviceToken.map { String(format: "%02x", $0) }.joined()
Kixo.setPushToken(token)
}Feature flags
let variant = Kixo.getFeatureFlag("new_checkout")
if variant == "enabled" {
showNewCheckout()
}Resilience model
The SDK ships with a four-stage state machine that makes it safe to embed in production iOS apps:
- initialising — set the moment
Kixo.startruns. Events that auto-trackers fire before the first config response are dropped on the floor (the server may saypaused: truefor the project or this SDK version, and we shouldn't buffer events for a session we're about to silence). - running — first config response succeeds. Events flow normally. Failed batches retry on a 5 / 10 / 20 / 30 / 60 s schedule; 10 consecutive failures move to a 60 s heartbeat; 30 failures move to a 30-minute cooldown.
- recovering — instead of hammering the batch endpoint forever during cooldown, the SDK pauses and re-fetches
/api/sdk/init. If the server returnspaused: truemid-outage, the SDK transitions topausedByServer. - pausedByServer — events drop at
enqueuetime. The SDK polls config periodically and resumes on un-pause.
The local queue caps at 200 events with FIFO drop-oldest, so a long offline window can never exhaust process memory. Retry delays carry ±25 % jitter to spread reconnects across the fleet.
Diagnostics
Read-only health snapshot. Useful in debug screens or smoke tests — answers "why aren't my events flowing?" without a debugger.
let diag = Kixo.diagnostics()
print(diag.queue.bufferedEventCount) // events waiting to flush
print(diag.queue.retryTier) // healthy | rapid | slow | cooldown
print(diag.paused) // server kill-switch state
print(diag.environment) // resolved env
print(diag.apiHost) // resolved ingest hostForce flush (for tests)
Synchronous overload that blocks up to timeout seconds on a flush completing. Designed for XCTest fixtures — never call from the main thread.
func testEventLanded() {
Kixo.track("test_event")
let landed = Kixo.flush(timeout: 5.0)
XCTAssertTrue(landed)
}Reset
Clear identity, super-properties, and the persisted queue. Call on logout so subsequent events aren't attributed to the previous user.
Kixo.reset()