> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tell.rs/llms.txt
> Use this file to discover all available pages before exploring further.

# Swift SDK

> Track events and logs from iOS, macOS, and visionOS apps.

The Swift SDK tracks events and structured logs from your Apple apps. It's a client SDK — you configure a singleton once at launch, and it handles device IDs, sessions, and delivery automatically.

Events called before initialization completes are queued and replayed. Your app never blocks on network I/O.

## Installation

Add the package in Xcode: **File > Add Package Dependencies** and enter:

```
https://github.com/tell-rs/tell-swift.git
```

Or in your `Package.swift`:

```swift theme={null}
dependencies: [
    .package(url: "https://github.com/tell-rs/tell-swift.git", from: "1.0.0")
]
```

Requires iOS 16.0+, macOS 13.0+, or visionOS 1.0+, with Swift 6.0+ and Xcode 16.0+.

## Quick start

```swift theme={null}
import Tell

// Configure once at app startup
Tell.shared.configure(apiKey: "a1b2c3d4e5f60718293a4b5c6d7e8f90", preset: .production)

// Track events
Tell.shared.track(.pageViewed, properties: ["url": "/home"])

// Identify users
Tell.shared.identify("user_123", traits: ["name": "Jane", "plan": "pro"])
```

That's it. The SDK generates a device ID, starts a session, collects device context, and batches events over TCP. Events fired before `configure` completes are queued (up to 1,000) and sent once ready.

## Configuration

Use a preset for one-line setup, or pass individual parameters.

```swift theme={null}
// Production — collect.tell.rs:50000, batch 100, flush 10s
Tell.shared.configure(apiKey: "YOUR_API_KEY", preset: .production)

// Development — localhost:50000, batch 10, flush 2s
Tell.shared.configure(apiKey: "YOUR_API_KEY", preset: .development)
```

For fire-and-forget initialization (returns immediately, connects in the background):

```swift theme={null}
Tell.shared.configureAsync(
    apiKey: "YOUR_API_KEY",
    endpoint: "collect.tell.rs:50000",
    batchSize: 100,
    flushInterval: 10,
    logLevel: .debug
)
```

For async initialization with error handling:

```swift theme={null}
try await Tell.shared.configure(
    apiKey: "YOUR_API_KEY",
    sessionTimeout: 30 * 60,
    defaultOptOut: false,
    onError: { error in print("Tell: \(error)") }
)
```

The API key must be a 32-character hex string.

## Tracking events

No user ID needed — the SDK tracks the current user automatically (anonymous until you call `identify`).

```swift theme={null}
// Typed event names
Tell.shared.track(.featureUsed, properties: [
    "feature": "dark_mode",
    "enabled": true
])

// String event names
Tell.shared.track("Custom Event", properties: ["key": "value"])

// No properties
Tell.shared.track(.pageViewed)
```

### Standard event names

The SDK provides strongly typed constants for common events:

```swift theme={null}
Tell.shared.track(.pageViewed, properties: ["url": "/pricing"])
Tell.shared.track(.userSignedUp, properties: ["source": "organic"])
Tell.shared.track(.orderCompleted, properties: ["total": 99.00])
Tell.shared.track(.featureUsed, properties: ["feature": "export"])
Tell.shared.track(.subscriptionStarted, properties: ["plan": "pro"])
```

Constants cover user lifecycle, revenue, subscriptions, trials, shopping, engagement, communication, and session events. Custom string names always work too.

### Revenue

Track purchases with typed currency codes:

```swift theme={null}
Tell.shared.revenue(
    amount: 49.99,
    currency: .usd,
    orderID: "order_456",
    properties: ["product": "annual_plan"]
)
```

The SDK includes constants for 60+ currencies (`.usd`, `.eur`, `.gbp`, `.jpy`, `.btc`, etc.) and accepts custom currency strings via `Currency("XYZ")`.

### User identity

```swift theme={null}
// Identify a known user — stores the ID for all subsequent events
Tell.shared.identify("user_123", traits: [
    "name": "Jane",
    "email": "jane@example.com",
    "plan": "pro"
])

// Associate user with a group
Tell.shared.group("company_456", properties: [
    "name": "Acme Corp",
    "plan": "enterprise"
])

// Link two identities
Tell.shared.alias("anon_visitor_abc", userId: "user_123")
```

### Super properties

Register properties that get merged into every `track`, `group`, and `revenue` call:

```swift theme={null}
Tell.shared.register(["app_version": "2.1.0", "env": "production"])

// This track call automatically includes app_version and env
Tell.shared.track(.featureUsed, properties: ["feature": "export"])

// Remove a super property
Tell.shared.unregister("env")
```

Event-specific properties override super properties when keys conflict.

## Structured logging

Send logs alongside events through the same pipeline:

```swift theme={null}
Tell.shared.logError("Payment failed", service: "billing", data: [
    "error": "card_declined",
    "amount": 9.99
])

Tell.shared.logInfo("User signed in", service: "auth", data: [
    "method": "oauth"
])

Tell.shared.logWarning("High memory usage", service: "app")
```

The `service` parameter defaults to `"app"`. Convenience methods are available for all nine RFC 5424 levels: `logEmergency`, `logAlert`, `logCritical`, `logError`, `logWarning`, `logNotice`, `logInfo`, `logDebug`, and `logTrace`.

## Sessions

Sessions are managed automatically. The SDK:

1. Generates a persistent device ID (stored in UserDefaults)
2. Creates a new session on launch with a device context snapshot
3. Tracks app background/foreground transitions
4. Starts a new session if the app was backgrounded longer than `sessionTimeout` (default: 30 minutes)

Device context collected automatically includes device model, OS version, app version, screen dimensions, locale, timezone, and battery state (iOS).

## Privacy

```swift theme={null}
// Stop all data collection — events are dropped locally
Tell.shared.optOut()

// Resume data collection
Tell.shared.optIn()

// Check current state
if Tell.shared.isOptedOut() {
    // Show consent prompt
}
```

Opt-out state is persisted across sessions. For privacy-first apps, set `defaultOptOut: true` during configuration so new users start opted out.

When opted out, all tracking and logging calls silently drop. `flush()` and `close()` still work.

## Lifecycle

```swift theme={null}
// Force-send all queued events
try await Tell.shared.flush()

// Reset user — generates new anonymous ID, clears super properties
Tell.shared.reset()

// Graceful shutdown
try await Tell.shared.close()
```

Call `reset()` on logout to start a fresh anonymous session for the next user.

## Advanced

### Configuration reference

| Parameter          | Default                 | Description                         |
| ------------------ | ----------------------- | ----------------------------------- |
| `endpoint`         | `collect.tell.rs:50000` | TCP collector address               |
| `batchSize`        | 100                     | Events per batch before auto-flush  |
| `flushInterval`    | 10s                     | Time between auto-flushes           |
| `sessionTimeout`   | 30 min                  | Background time before new session  |
| `defaultOptOut`    | false                   | Initial opt-out state for new users |
| `generateEventIds` | false                   | Client-side event deduplication IDs |
| `logLevel`         | `.info`                 | SDK internal log verbosity          |

### Validation rules

| Field          | Rule                    |
| -------------- | ----------------------- |
| API key        | 32-character hex string |
| Event name     | 1–256 characters        |
| Log message    | 1–65,536 characters     |
| User ID        | Non-empty               |
| Group ID       | Non-empty               |
| Order ID       | Non-empty               |
| Revenue amount | Positive number         |

### Pre-initialization queue

Events called before `configure` completes are buffered (up to 1,000 items). On overflow, the oldest event is dropped. Once initialization succeeds, all queued events are replayed in order.
