> ## Documentation Index
> Fetch the complete documentation index at: https://mixpanel-edb78807-fix-roles-permissions-table-width.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Mixpanel SDKs: iOS (Swift)

## Getting Started

Please refer to our [Quickstart Guide](/docs/quickstart/connect-your-data?sdk=swift).

The [Full API Reference](https://mixpanel.github.io/mixpanel-swift/), [Library Source Code](https://github.com/mixpanel/mixpanel-swift), and an [Example Application](https://github.com/mixpanel/mixpanel-swift/tree/master/MixpanelDemo/MixpanelDemo) are documented in our GitHub repo.

## Installing the Library

You can install the Swift library by using CocoaPods, Carthage, or Swift Package Manager.

<Tabs>
  <Tab title="Swift Package Manager">
    1. In Xcode, select File > Add Packages...
    2. Enter the package URL for [this repository](https://github.com/mixpanel/mixpanel-swift) and select version v2.8.0 or above.
  </Tab>

  <Tab title="CocoaPods">
    1. Install CocoaPods by typing the following in your command line:

    ```bash theme={null}
    # install CocoaPods
    gem install cocoapods
    ```

    2. After installing CocoaPods, create a local CocoaPods spec mirror by running the following:

    ```bash theme={null}
    # Create CocoaPods spec mirror
    pod setup
    ```

    3. Then navigate to your Xcode project directory and create a Podfile by running the following:

    ```bash theme={null}
    # create Podfile in project directory
    pod init
    ```

    4. Open the Podfile that was generated and add the Mixpanel library to your dependencies:

    ```ruby Ruby theme={null}
    target 'MyApp' do
      pod 'Mixpanel-swift'
    end
    ```

    5. Install the Mixpanel library and create a new Xcode workspace by running the following in the Xcode project directory:

    ```bash theme={null}
    // install library in your dependencies
    pod install
    ```
  </Tab>

  <Tab title="Carthage">
    Mixpanel supports [Carthage](https://github.com/Carthage/Carthage#quick-start) to package your dependencies as a framework. Include the following dependency in your Cartfile:

    ```bash theme={null}
    # add Mixpanel library to your dependencies
    github "mixpanel/mixpanel-swift"
    ```
  </Tab>
</Tabs>

After installing the library, import `Mixpanel` into `AppDelegate.swift` and initialize Mixpanel within [`application:didFinishLaunchingWithOptions:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application) using your [project token](/docs/orgs-and-projects/managing-projects#find-your-project-tokens).

```swift Swift theme={null}
/// import Mixpanel class from the library
import Mixpanel

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    ...
    /// initialize Mixpanel using your project token
    let options = MixpanelOptions(token: "YOUR_PROJECT_TOKEN")
    Mixpanel.initialize(options: options)
    ...
}
```

<span id="library-configuration" />

### SDK Configuration Using MixpanelOptions

<Warning>
  **Data Residency:** For projects with EU or India data residency, you must configure the SDK to use the correct [regional endpoint](#data-residency). Events sent to the wrong region will not be ingested. Learn more about [Privacy-Friendly Tracking](/docs/tracking-methods/sdks/swift#privacy-friendly-tracking)
</Warning>

Configure the Mixpanel SDK using [`MixpanelOptions`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) to customize tracking behavior, data residency, privacy settings, and more.

#### Configuration Reference

| Config Option                | Description                                                                                                                                                               | Default                              |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `token`                      | Your Mixpanel project token (required)                                                                                                                                    | N/A                                  |
| `serverURL`                  | Custom API endpoint. **Required** for [EU](#eu-data-residency) or [India](#india-data-residency) data residency. Takes precedence over `proxyServerConfig.serverUrl`      | US (`https://api.mixpanel.com`)      |
| `flushInterval`              | Interval in seconds between automatic flush operations. Set to `0` to disable. See [Flushing Events](#flushing-events)                                                    | `60`                                 |
| `trackAutomaticEvents`       | Auto-track app lifecycle events. **Not recommended** — see [Legacy Automatically Tracked Events](#legacy-automatically-tracked-events)                                    | `false`                              |
| `optOutTrackingByDefault`    | Start with tracking disabled for GDPR compliance. See [Opt Out of Tracking](#opt-out-of-tracking)                                                                         | `false`                              |
| `useIPAddressForGeoLocation` | Enable IP-based geolocation. See [Disable Geolocation](#disable-geolocation)                                                                                              | `true`                               |
| `useUniqueDistinctId`        | Use device IDFV instead of random UUID. See [Custom Device ID Generation](#custom-device-id-generation)                                                                   | `false`                              |
| `instanceName`               | Unique identifier for multi-instance setups                                                                                                                               | Project token                        |
| `deviceIdProvider`           | Custom closure for device ID generation (requires v5.2.0+). See [Custom Device ID Generation](#custom-device-id-generation)                                               | `nil`                                |
| `superProperties`            | Properties automatically added to all events. Can also be set at runtime. See [Setting Super Properties](#setting-super-properties)                                       | `nil`                                |
| `featureFlagOptions`         | Feature flags configuration (prefetch behavior and custom context). See [Feature Flags Initialization](/docs/tracking-methods/sdks/swift/swift-flags#flag-initialization) | `FeatureFlagOptions(enabled: false)` |
| `proxyServerConfig`          | Proxy configuration with headers and query params                                                                                                                         | `nil`                                |
| `useGzipCompression`         | Enable gzip compression for network requests                                                                                                                              | `true`                               |

<Note>
  **Best Practice:** Configure all settings (especially `serverURL` for data residency) during initialization using `MixpanelOptions`. This ensures all SDK components (events, profiles, feature flags) use the correct configuration from the start, avoiding potential race conditions.
</Note>

After initialization, you can further customize the instance using methods on [`MixpanelInstance`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html).

**Common Configuration Example**

```swift Swift theme={null}
import Mixpanel

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    ...
    /// Configure Mixpanel for EU data residency
    let options = MixpanelOptions(
        token: "YOUR_PROJECT_TOKEN",
        serverURL: "https://api-eu.mixpanel.com",
        flushInterval: 15
    )
    Mixpanel.initialize(options: options)

    /// Enable debug logging after initialization
    Mixpanel.mainInstance().loggingEnabled = true
    ...
}
```

See detailed explanations of each option in the sections below.

## Sending Events

Use [`.track(event:properties:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC5track5event10propertiesySSSg_SDySSAA0A4Type_pGSgtF) to send an event by providing the event name and any event properties. This will trigger a request to the [/track API endpoint](/reference/track-event) to ingest the event into your project.

<Note>
  The [/track endpoint](/reference/track-event) will only validate events with timestamps within the last 5 days of the request. Events with timestamps older than 5 days will not be ingested. See below on best practices for historical imports.
</Note>

**Example Usage**

```swift Swift theme={null}
/// track "some_event" 
/// with "plan" event prop set to "Premium"
Mixpanel.mainInstance().track(event: "some_event",
    properties:["Plan": "Premium"])
```

### Timing Events

You can track the time it took for an action to occur, such as an image upload or a comment post, using [`.time(event:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:FC8Mixpanel16MixpanelInstance4timeFT5eventSS_T_). This will mark the "start" of your action, which you can then finish with a track call. The time duration is then recorded in the "Duration" property.

```swift Swift theme={null}
/// start the timer for the event "Image Upload"
Mixpanel.mainInstance().time(event: "Image Upload")

/// 20 seconds later...

/// track "Image Upload" event with "Duration" event prop set to 20
Mixpanel.mainInstance().track(event: "Image Upload")
```

### Flushing Events

To preserve battery life and customer bandwidth, the Mixpanel library doesn't send the events you record immediately. Instead, it sends batches to the Mixpanel servers every 60 seconds while your application is running, as well as when the application transitions to the background.

Call [`.flush()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC5flush16performFullFlush10completionySb_yycSgtF) manually if you want to force a flush at a particular moment.

**Example Usage**

```swift Swift theme={null}
/// flush batched events for ingestion immediately
Mixpanel.mainInstance().flush()
```

#### Configuring Flush Interval

The default flush interval is 60 seconds. You can customize this using the [`flushInterval`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) option in `MixpanelOptions` (see [Configuration Reference](#configuration-reference)). Set to `0` to disable automatic flushing entirely (requires manual `.flush()` calls).

**Recommended: Configure During Initialization**

```swift Swift theme={null}
/// Set flush interval to 120 seconds during initialization
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    flushInterval: 120  // Flush every 2 minutes
)
Mixpanel.initialize(options: options)
```

**Alternative: Update After Initialization** You can also update the flush interval dynamically using [`flushInterval`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC13flushIntervalSdvp).

```swift Swift theme={null}
/// Change flush interval at runtime
Mixpanel.mainInstance().flushInterval = 120
```

#### Importing Historical Events

The Swift SDK is a tracking SDK designed for real-time tracking in a client-side environment. Calling [`.track(event:properties:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC5track5event10propertiesySSSg_SDySSAA0A4Type_pGSgtF) triggers a request to our [/track API endpoint](/reference/track-event), which will validate for events with a timestamp that is within the last 5 days of the request. **Events older than 5 days will not be ingested**.

For bulk import of historical events older than 5 days, we will need to use the [/import API endpoint](/reference/import-events) which is optimized for scripting and supports ingesting historical data. We recommend the [Python SDK](/docs/tracking-methods/sdks/python) (see the [`.import_data()`](https://mixpanel.github.io/mixpanel-python/#primary-interface) function) and [mixpanel-utils module](https://github.com/mixpanel/mixpanel-utils) (see the [`import_events()`](https://github.com/mixpanel/mixpanel-utils?tab=readme-ov-file#import-events) function) which both leverages the /import API for event ingestion.

## Setting Super Properties

Super properties are global event properties that you define once and apply to all events. You can set super properties during SDK initialization or at runtime.

### Setting Super Properties During Initialization

You can set initial super properties using the [`superProperties`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) option in `MixpanelOptions` (see [Configuration Reference](#configuration-reference)).

**Example Usage**

```swift Swift theme={null}
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    superProperties: [
        "app_version": "1.2.3",
        "platform": "iOS"
    ]
)
Mixpanel.initialize(options: options)

/// track "some_event"
/// "app_version" and "platform" are automatically added as event props
Mixpanel.mainInstance().track(event: "some_event")
```

### Setting Super Properties at Runtime

To register super properties at runtime, call [`.registerSuperProperties(_:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC23registerSuperPropertiesyySDySSAA0A4Type_pGF).

Use [`.registerSuperPropertiesOnce(_:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC27registerSuperPropertiesOnce_12defaultValueySDySSAA0A4Type_pG_AaF_pSgtF) to register super properties without overwriting existing values.

**Example Usage**

```swift Swift theme={null}
/// register a "name" super property
Mixpanel.mainInstance().registerSuperProperties(["name": "Sam"])

/// track "some_event"
/// "name" is automatically added as an event prop
Mixpanel.mainInstance().track(event: "some_event")
```

**Best Practice: Batch Multiple Properties**

Creating properties involves an async operation to local storage. If you need to create multiple properties at once, it's best to send a single call to the register function with all properties at once, to avoid possible issues with race conditions overwriting each other.

**Example Usage**

```swift Swift theme={null}
Mixpanel.mainInstance().registerSuperProperties([
    "superProp0": "value1",
    "superProp1": "value2",
    "superProp2": "value3",
    "superProp3": 1,
    "superProp4": 2,
    "superProp5": 3
])

Mixpanel.mainInstance().track(event: "test")
```

See more methods related to super properties in the complete library reference [here](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html).

## Managing User Identity

You can handle the identity of a user using the [`.identify()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC8identify10distinctId9usePeople10completionySS_SbyycSgtF) and [`.reset()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC5reset10completionyyycSg_tF) methods. Learn more about [identity management](/docs/tracking-methods/id-management/identity-management) and [identifying users](/docs/tracking-methods/id-management/identifying-users).

### Identify

<Warning>
  We recommend against calling `.identify()` for anonymous visitors to your site.
</Warning>

Call [`.identify()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC8identify10distinctId9usePeople10completionySS_SbyycSgtF) when you know the identity of the current user, passing in their user ID as an argument. This is typically at account registration and at log in.

```swift Swift theme={null}
/// your user signs in and tracks a sign in event
Mixpanel.mainInstance().track(event: "sign in")

/// upon sign in, identify the user with their ID
/// ensure future events sent from the user have distinct_id 12345
Mixpanel.mainInstance().identify(distinctId: "12345");
```

### Call Reset on Logout

Call [`.reset()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC5reset10completionyyycSg_tF) to clear data attributed to a user when they logout. This will clear the local storage and allows you to handle [multiple users on a single device](/docs/tracking-methods/id-management/identifying-users-simplified#multiple-users-one-device).

**Example Usage**

```swift Swift theme={null}
/// your user signs out and tracks a log out event
Mixpanel.mainInstance().track(event: "log out")

/// upon logout, call reset to clear local storage
/// new distinct_id is generated for events moving forward
Mixpanel.mainInstance().reset()
```

<Note>
  Since v2.7.7, Mixpanel stopped using the IFA (ID for Advertisers) as a distinct\_id. Calls to `.reset()` will generate a new random UUID unless you've configured custom device ID behavior. See [Custom Device ID Generation](#custom-device-id-generation) for details on using IDFV or implementing custom device ID logic.
</Note>

### Custom Device ID Generation

The Swift SDK provides two ways to customize device ID generation (see [Configuration Reference](#configuration-reference)):

1. **`useUniqueDistinctId`** - Use device IDFV (Identifier for Vendor) instead of random UUID
2. **`deviceIdProvider`** - Custom closure for implementing your own device ID generation logic

<Warning>
  Customizing device ID generation is an architectural decision that should be made at project inception. Changing this after deployment can cause user identity management issues.
</Warning>

#### Using Device IDFV

Set [`useUniqueDistinctId`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) to `true` to use the device's IDFV instead of a random UUID. The IDFV persists across app updates but changes when the app is uninstalled and reinstalled.

**Example Usage**

```swift Swift theme={null}
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    useUniqueDistinctId: true  // Use IDFV instead of UUID
)
Mixpanel.initialize(options: options)
```

<Note>
  You can also enable IDFV by setting `MIXPANEL_UNIQUE_DISTINCT_ID` in build settings `Active Compilation Conditions` on the Mixpanel framework target. When using IDFV, the ID will not change when you call `.reset()`, but will change upon app uninstalls/reinstalls.
</Note>

#### Custom Device ID Provider

<Note>
  The `deviceIdProvider` option requires Swift SDK [v5.2.0](https://github.com/mixpanel/mixpanel-swift/releases/tag/v5.2.0) or later.
</Note>

For advanced use cases, use the [`deviceIdProvider`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) closure to implement custom device ID generation logic. This allows you to control where device IDs are stored and whether they persist across `.reset()` calls.

If the provider returns `nil` or an empty string, the SDK will fall back to generating a default device ID (UUID or IDFV based on `useUniqueDistinctId`).

**Example Usage**

```swift Swift theme={null}
// create a device ID provider
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    deviceIdProvider: {
        return getYourCustomDeviceId()
    }
)

let mixpanel = Mixpanel.initialize(options: options)
```

**Persistent vs Ephemeral IDs**

Returning the same value each time the provider is called creates a persistent device ID that survives `.reset()` calls. Returning different values generates a new device ID on each reset.

```swift Swift theme={null}
// persistent ID - survives reset() calls
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    deviceIdProvider: {
        return getStoredDeviceId() // returns same value every time
    }
)

// ephemeral ID - changes on reset() calls
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    deviceIdProvider: {
        return generateNewDeviceId() // returns new value each time
    }
)
```

## Storing User Profiles

Once your users are identified, create [user profiles](/docs/data-structure/user-profiles) by setting profile properties to describe them. Example profile properties include "name", "email", "company", and any other demographic details about the user.

The Swift SDK provides a few methods for setting profile properties under the [`People`](https://mixpanel.github.io/mixpanel-swift/Classes/People.html) class accessible via [`.people()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC6peopleAA6PeopleCSgvp). These methods will trigger requests to the [/engage API endpoint](/reference/profile-set).

### Setting Profile Properties

<Note>
  You must call `.identify()` before setting profile properties in order to associate the profile properties you set with the target user. If identify is not called, the profile update will be queued for ingestion until an identify call is made.
</Note>

You can set properties on a user profile with [`people.set(property:to:)`](https://mixpanel.github.io/mixpanel-swift/Classes/People.html#/s:FC8Mixpanel6People3setFT8propertySS2toPs9AnyObject__T_).

If a profile property already exists, it will be overwritten with the latest value provided in the method. If a profile property does not exist, it will be added to the profile.

**Example Usage**

```swift Swift theme={null}
/// You must call identify to associate the profile update with the user
Mixpanel.mainInstance().identify(distinctId: "12345")

/// Set user's "Plan" profile prop to "Premium"
Mixpanel.mainInstance().people.set(properties: ["plan":"Premium", "$email":"joe.doe@example.com"])

/// Update "plan" to "Enterprise"
Mixpanel.mainInstance().people.set(properties: ["plan":"Enterprise"])
```

### Other Types of Profile Updates

There are a few other methods for setting profile properties. See a complete reference of the available methods [here](https://mixpanel.github.io/mixpanel-swift/Classes/People.html)

A few commonly used people methods are highlighted below:

<Tabs>
  <Tab title="setOnce()">
    The [`setOnce(properties:)`](https://mixpanel.github.io/mixpanel-swift/Classes/People.html#/s:8Mixpanel6PeopleC7setOnce10propertiesySDySSAA0A4Type_pG_tF) method set profile properties only if they do not exist yet. If it is setting a profile property that already exists, it will be ignored.

    Use this method if you want to set profile properties without the risk of overwriting existing data.

    **Example Usage**

    ```swift Swift theme={null}
    /// set profile properties for user "12345"
    Mixpanel.mainInstance().identify(distinctId: "12345")
    Mixpanel.mainInstance().people.set(properties: ["name":"Sam"])

    /// will be ignored since "name" already exists
    Mixpanel.mainInstance().people.setOnce(properties: ["name":"Samantha"])

    /// set "location" property since it does not exist
    Mixpanel.mainInstance().people.setOnce(properties: ["location":"us"])
    ```
  </Tab>

  <Tab title="append()">
    The [`append(properties:)`](https://mixpanel.github.io/mixpanel-swift/Classes/People.html#/s:8Mixpanel6PeopleC6append10propertiesySDySSAA0A4Type_pG_tF) method appends values to a list profile property.

    Use this method to add additional values to an existing list property instead of redefining the entire list.

    **Example Usage**

    ```swift Swift theme={null}
    /// set profile properties for user "12345"
    Mixpanel.mainInstance().identify(distinctId: "12345")
    Mixpanel.mainInstance().people.set(properties: [
        "roles":["sales","engineer"]
        ])


    /// add "legal" to "roles"
    /// new "roles" values are ['sales','engineer','legal']
    Mixpanel.mainInstance().people.append(properties: [
        "roles":"legal"
        ])

    /// append() allows duplicates
    // new "roles" values are ['sales','engineer','legal','legal']
    Mixpanel.mainInstance().people.append(properties: [
        "roles":"legal"
        ])
    ```
  </Tab>

  <Tab title="union()">
    The [`union(properties:)`](https://mixpanel.github.io/mixpanel-swift/Classes/People.html#/s:8Mixpanel6PeopleC5union10propertiesySDySSAA0A4Type_pG_tF) method appends new values to a list property, excluding duplicates.

    Use this method to create a list profile property that only contains unique values without duplicates.

    **Example Usage**

    ```swift Swift theme={null}
    /// set profile properties for user "12345"
    Mixpanel.mainInstance().identify(distinctId: "12345")
    Mixpanel.mainInstance().people.set(properties: [
        "roles":["sales","engineer"]
        ])

    /// will be ignored since "engineer" already exists in "roles"
    Mixpanel.mainInstance().people.union(properties: [
        "roles":"engineer"
        ])

    /// add "legal" to "roles"
    /// "roles" = ['sales','engineer','legal']
    Mixpanel.mainInstance().people.union(properties: [
        "roles":"legal"
        ])
    ```
  </Tab>

  <Tab title="increment()">
    The [`increment(property:by:)`](https://mixpanel.github.io/mixpanel-swift/Classes/People.html#/s:8Mixpanel6PeopleC9increment8property2byySS_SdtF) method increments a numeric property by a whole number.

    Use this method to add to or subtract from your numeric property based on its current value.

    **Example Usage**

    ```swift Swift theme={null}
    /// set profile properties for user "12345"
    Mixpanel.mainInstance().identify(distinctId: "12345")
    Mixpanel.mainInstance().people.set(properties: ["age":25])

    /// increment "age" by 2
    Mixpanel.mainInstance().people.increment(property: "age", by: 2)

    /// use negative number to decrement
    /// decrement "age" by 5
    Mixpanel.mainInstance().people.increment(property: "age", by: -5)
    ```
  </Tab>
</Tabs>

## Group Analytics

<Note>
  Read more about [Group Analytics](/docs/data-structure/group-analytics) before proceeding. You will need to have the [group key defined in your project settings](/docs/data-structure/group-analytics#group-keys-in-project-settings) first.
</Note>

Mixpanel Group Analytics is a paid add-on that allows behavioral data analysis by selected groups, as opposed to individual users.

A group is identified by the `group_key` and `group_id`.

* `group_key` is the event property that connects event data to a group. (e.g. `company`)
* `group_id` is the identifier for a specific group. (e.g. `mixpanel`,`company_a`,`company_b`, etc.)

The Swift SDK provides a few methods for adding individual users to a group and setting group profile properties.

### Adding Users to a Group

[All events must have the group key as an event property in order to be attributed to a group](/docs/data-structure/group-analytics#group-keys-tracked-as-event-properties). Without the group key, an event cannot be attributed to a group.

Call the [`setGroup(groupKey:groupIDs:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC8setGroup8groupKey0E2IDySS_AA0A4Type_ptF) to register the current user to a group, which would add the `group_key` as an event property set to the `group_id` value to all events moving forward.

**Example Usage**

```swift Swift theme={null}
/// assign the current user to the "mixpanel" company group
Mixpanel.mainInstance().setGroup(groupKey: "company", groupID: ["mixpanel"])

/// track "some_event"
/// event prop "company" = ["mixpanel"] is added automatically
Mixpanel.mainInstance().track(event: "some_event")


/// alternatively you can define the group key manually
Mixpanel.mainInstance().track(event: "some_event", properties: [
    "company": ["mixpanel"]])
```

**Multiple Groups**

[An event can be attributed to multiple groups](/docs/data-structure/group-analytics#attribute-events-to-multiple-groups) by passing in the `group_key` value as a list of multiple `group_id` values.

Call [`addGroup(groupKey:groupID:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC8addGroup8groupKey0E2IDySS_AA0A4Type_ptF) to add additional `group_id`s to an existing group key property.

**Example Usage**

```swift Swift theme={null}
/// assign the current user to the "mixpanel" company group
Mixpanel.mainInstance().setGroup(groupKey: "company", groupID: ["mixpanel"])

/// add "mp-us" as an additional company group
Mixpanel.mainInstance().addGroup(groupKey: "company", groupID: "mp-us")

/// track "some_event"
/// event prop "company" = ["mixpanel","mp-us"] is added automatically
Mixpanel.mainInstance().track(event: "some_event")
```

### Adding Group Identifiers to User Profiles

To connect group information to a user profile, include the `group_key` and `group_id` as a user profile property using the `.people.set()` call.

**Example Usage**

```swift Swift theme={null}
/// You must call identify to associate the profile update with the user
Mixpanel.mainInstance().identify(distinctId: "12345")

/// set group key "company" as a user prop
/// with group id "mixpanel" as value
Mixpanel.mainInstance().people.set(properties: ["company":"mixpanel", "$email":"joe.doe@example.com"])
```

### Setting Group Profile Properties

Create a group profiles by setting group properties, similar to a user profile. For example, you may want to describe a company group with properties such as "ARR", "employee\_count", and "subscription".

To set group profile properties, specify the group that needs to be updated by calling [`getGroup(groupKey:groupID:)`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC8getGroup8groupKey0E2IDAA0D0CSS_AA0A4Type_ptF), then set the group properties by chaining the [`set(properties:)`](https://mixpanel.github.io/mixpanel-swift/Classes/Group.html#/s:8Mixpanel5GroupC3set10propertiesySDySSAA0A4Type_pG_tF) method, which will trigger a request to the [/groups API endpoint](/reference/group-set-property).

**Example Usage**

```swift Swift theme={null}
/// assign the current user to the "mixpanel" company group
Mixpanel.mainInstance().setGroup(groupKey: "company", groupID: ["mixpanel"])

/// specify the target group using the group_key and group_id using getGroup()
/// set "industry" as a group profile prop to "analytics" using set()
Mixpanel.mainInstance().getGroup(groupKey: "Company", groupID: “mixpanel”).set(properties: [
    "industry": "analytics"
])
```

### Other Group Profile Methods

See all of the methods under the MixpanelGroup class [here](https://mixpanel.github.io/mixpanel-swift/Classes/Group.html).

A few commonly used group methods are highlighted below:

<Tabs>
  <Tab title="setOnce()">
    The [`setOnce(properties:)`](https://mixpanel.github.io/mixpanel-swift/Classes/Group.html#/s:8Mixpanel5GroupC7setOnce10propertiesySDySSAA0A4Type_pG_tF) method set group profile properties only if they do not exist yet. If it is setting a profile property that already exists, it will be ignored.

    Use this method if you want to set group profile properties without the risk of overwriting existing data.

    **Example Usage**

    ```swift Swift theme={null}
    /// assign the current user to the "mixpanel" company group
    Mixpanel.mainInstance().setGroup(groupKey: "company", groupID: ["mixpanel"])

    /// set group profile properties
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).set(properties: [
        "name": "Mixpanel"])

    /// ignored since "name" already exists
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).setOnce(properties: [
        "name": "mp"])

    /// set "country" group prop since it does not exist
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).setOnce(properties: [
        "country": "us"])
    ```
  </Tab>

  <Tab title="unset()">
    The [`unset(property:)`](https://mixpanel.github.io/mixpanel-swift/Classes/Group.html#/s:8Mixpanel5GroupC5unset8propertyySS_tF) method removes a group property from a group profile.

    Use this method to delete unwanted group profile properties from a specific group profile.

    **Example Usage**

    ```swift Swift theme={null}
    /// assign the current user to the "mixpanel" company group
    Mixpanel.mainInstance().setGroup(groupKey: "company", groupID: ["mixpanel"])

    /// set group profile properties
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).set(properties: [
        "name": "Mixpanel",
        "employee_count": 100
    ])

    /// delete "employee_count" from the group profile
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).unset(properties: "employee_count")

    /// The group only has the "name" property remaining
    ```
  </Tab>

  <Tab title="union()">
    The [`union(key:values:)`](https://mixpanel.github.io/mixpanel-swift/Classes/Group.html#/s:8Mixpanel5GroupC5union3key6valuesySS_SayAA0A4Type_pGtF) method append new values to a list property, excluding duplicates.

    Use this method to create a list profile property that only contains unique values without duplicates.

    **Example Usage**

    ```swift Swift theme={null}
    /// assign the current user to the "mixpanel" company group
    Mixpanel.mainInstance().setGroup(groupKey: "company", groupID: ["mixpanel"])

    /// set group profile properties
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).set(properties: [
        "name": "Mixpanel",
        "features": ["reports","alerts","cohorts"]
    ])

    /// add "data pipeline" to "features" prop
    /// ignore "alerts" since it is a duplicate value
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).union(key: "features",
        values: ["data pipeline","alerts"])
    ```
  </Tab>

  <Tab title="remove()">
    The [`remove(key:value:)`](https://mixpanel.github.io/mixpanel-swift/Classes/Group.html#/s:8Mixpanel5GroupC6remove3key5valueySS_AA0A4Type_ptF) method removes a value from a list-valued group profile property.

    Use this method to remove specific values from a list without affecting all of the other values in the list.

    **Example Usage**

    ```swift Swift theme={null}
    /// assign the current user to the "mixpanel" company group
    Mixpanel.mainInstance().setGroup(groupKey: "company", groupID: ["mixpanel"])

    /// set group profile properties
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).set(properties: [
        "name": "Mixpanel",
        "features": ["reports","alerts","cohorts"]
    ])

    /// remove "alerts" from "features"
    Mixpanel.mainInstance().getGroup(groupKey: "company", groupID: “mixpanel”).remove(
        key: "features", value: "alerts")

    /// "features" = ["reports","cohorts"]
    ```
  </Tab>
</Tabs>

## Autocapture

The Mixpanel Swift SDK provides methods on the `autocapture` object to help you track common user interactions with minimal instrumentation. These methods fire standardized events that are recognized by the Mixpanel platform.

### Screen View & Screen Leave

Modern iOS apps use a variety of UI frameworks — UIKit with view controllers, SwiftUI with `NavigationStack`, or a mix of both. Each defines and transitions between "screens" differently, making it impractical for the SDK to automatically detect screen boundaries across all setups. Instead, you hook `trackScreenView` and `trackScreenLeave` into your app's navigation, giving you full control over what counts as a screen and when transitions happen.

Track screen navigations using `trackScreenView` and `trackScreenLeave`. These methods fire the following events:

| Method               | Mixpanel Event   |
| -------------------- | ---------------- |
| `trackScreenView()`  | `$mp_page_view`  |
| `trackScreenLeave()` | `$mp_page_leave` |

Both events automatically include `current_page_title` (the screen name) and `$mp_autocapture: true`.

<Tabs>
  <Tab title="Manual">
    Call these methods from any view controller or SwiftUI view:

    ```swift theme={null}
    Mixpanel.mainInstance().autocapture.trackScreenView(screenName: "HomeScreen")
    Mixpanel.mainInstance().autocapture.trackScreenLeave(screenName: "HomeScreen")
    ```
  </Tab>

  <Tab title="UIKit (Swizzling)">
    Swizzle `viewDidAppear` and `viewWillDisappear` once at launch to track every view controller automatically:

    ```swift theme={null}
    // In AppDelegate.didFinishLaunchingWithOptions:
    Mixpanel.initialize(token: "YOUR_TOKEN", trackAutomaticEvents: true)
    UIViewController.setupAutomaticScreenTracking()
    ```

    ```swift theme={null}
    extension UIViewController {
        static func setupAutomaticScreenTracking() {
            swizzle(#selector(viewDidAppear(_:)), with: #selector(mp_viewDidAppear(_:)))
            swizzle(#selector(viewWillDisappear(_:)), with: #selector(mp_viewWillDisappear(_:)))
        }

        @objc private func mp_viewDidAppear(_ animated: Bool) {
            mp_viewDidAppear(animated)
            let name = String(describing: type(of: self))
            guard !name.hasPrefix("UI"), !name.hasPrefix("_") else { return }
            Mixpanel.mainInstance().autocapture.trackScreenView(screenName: name)
        }

        @objc private func mp_viewWillDisappear(_ animated: Bool) {
            mp_viewWillDisappear(animated)
            let name = String(describing: type(of: self))
            guard !name.hasPrefix("UI"), !name.hasPrefix("_") else { return }
            Mixpanel.mainInstance().autocapture.trackScreenLeave(screenName: name)
        }

        private static func swizzle(_ original: Selector, with swizzled: Selector) {
            guard let m1 = class_getInstanceMethod(UIViewController.self, original),
                  let m2 = class_getInstanceMethod(UIViewController.self, swizzled)
            else { return }
            method_exchangeImplementations(m1, m2)
        }
    }
    ```
  </Tab>
</Tabs>

#### Passing Custom Properties

Both methods accept an optional `properties` parameter. For example, forwarding UTM params from a deep link:

```swift theme={null}
let queryItems = urlComponents.queryItems
let utmProps: Properties = [
    "utm_source": queryItems?.first(where: { $0.name == "utm_source" })?.value ?? "",
    "utm_medium": queryItems?.first(where: { $0.name == "utm_medium" })?.value ?? "",
    "utm_campaign": queryItems?.first(where: { $0.name == "utm_campaign" })?.value ?? ""
]
Mixpanel.mainInstance().autocapture.trackScreenView(
    screenName: "PromoScreen", properties: utmProps
)
```

### Click Events

<Note>
  **Beta.** Autocaptured click events are in beta: the events and the properties they capture may change before general availability. They require Swift SDK v6.6.0 or later, and are **disabled by default** — you opt in with `AutocaptureOptions` at initialization.
</Note>

Starting in v6.6.0, the SDK can capture taps for you, with no per-view instrumentation. Once enabled, it intercepts touches across every window in your app — including presented sheets and alerts — and works with both UIKit and SwiftUI. Three events are produced:

| Event            | Fires when                                            |
| ---------------- | ----------------------------------------------------- |
| `$mp_click`      | The user taps any element                             |
| `$mp_dead_click` | A tap on an interactive element produces no UI change |
| `$mp_rage_click` | The user taps rapidly and repeatedly in the same spot |

All three carry `$mp_autocapture: true`, alongside the properties described in [Tracked Properties](#tracked-properties).

#### Click

A `$mp_click` event is emitted for every tap that resolves to an element, whether or not that element handles the tap. Scrolls, swipes and long presses do not produce clicks.

When the tap lands on a non-interactive child — such as a `UILabel` inside a `UIButton` — the SDK walks up to the nearest interactive ancestor (up to 10 levels) and reports *that* element's identity. This keeps a button from being reported as its label. If no interactive ancestor is found, the tapped element's own identity is used.

#### Dead Click

A `$mp_dead_click` event is emitted when a tap on an **interactive** element produces no visible response. The SDK snapshots the UI immediately after the tap, then compares it **500 ms** later; if nothing has changed, the event is emitted. That time window is the default `DeadClickOptions.timeWindowMs` and is [configurable](#enabling-click-autocapture). A dead click is always accompanied by the `$mp_click` for the same tap.

Only interactive elements are candidates, so tapping a plain label or a decorative image never produces one. Controls with inherent visual feedback are excluded by type, because their response happens in a layer the detector cannot observe: `UITextField`, `UITextView`, `UISwitch`, `UISlider`, `UIStepper`, `UISegmentedControl`, `UIDatePicker`, `UIPickerView` and `UIPageControl`, plus the SwiftUI equivalents `TextField`, `TextEditor`, `SecureField`, `Toggle`, `Slider`, `Stepper`, `Picker` and `DatePicker`.

<Note>
  Detection is based on UI change only — the SDK does not observe network activity. A handler that fires a request but leaves the screen unchanged for the dead click time window is reported as a dead click, which is usually the user-perceived truth. Conversely, a response that only changes color, alpha or an animation may not register as a change.
</Note>

#### Rage Click

A `$mp_rage_click` event is emitted when **4 or more taps** land within **1000 ms** of each other and within **44 pt** on screen — the signature of a user hammering a control that isn't responding. These three thresholds are the defaults — `clickThreshold`, `timeWindowMs` and `radius` on `RageClickOptions` — and are [configurable](#enabling-click-autocapture). The event is emitted *in addition to* the individual `$mp_click` events, so four rapid taps produce four clicks and one rage click. Taps spread out in time or across the screen do not qualify.

Rage clicks are a **frustration signal**: alongside dead clicks, they surface the places where your app is failing users without throwing an error. Break them down by `$el_id` to find the specific controls people are fighting with, or use them to segment sessions worth watching in Session Replay.

#### One-Time Setup: Element IDs

Do this once, before you enable click autocapture. It isn't strictly required — autocapture works without it and every tap is still captured — but it's what makes the data readable. An element with no identifier reports a positional hash like `UIButton_a3f21c`: it groups correctly, but it tells you nothing in a report and changes if you reorder siblings.

So give every element you plan to measure a stable identifier first. In both frameworks the identifier belongs on the element that **handles the tap** — the SDK walks up to the nearest interactive ancestor and reads identity from there, so an identifier on a non-interactive wrapper is never used.

<CodeGroup>
  ```swift UIKit theme={null}
  let checkoutButton = UIButton(type: .system)
  checkoutButton.setTitle("Checkout", for: .normal)
  checkoutButton.accessibilityIdentifier = "checkout_button"
  ```

  ```swift SwiftUI theme={null}
  Button("Checkout") {
      // ...
  }
  .accessibilityIdentifier("checkout_button")
  ```
</CodeGroup>

* **UIKit:** For storyboard and XIB views, set the identifier in the **Identity inspector → Accessibility → Identifier** field instead of in code.
* **SwiftUI:** Reading the identifier back requires iOS 18 or later — see the warning under [Element ID Resolution](#element-id-resolution). On iOS 15–17 these elements fall back to the hash whatever you set, so plan your reports accordingly.
* **`accessibilityIdentifier` is not `accessibilityLabel`:** The identifier is developer-facing and never announced by VoiceOver, which is what makes it safe to use as an ID; the label is accessibility metadata and is never used as an identifier. Setting only a label leaves the element on the hash fallback.

Full precedence rules are in [Element ID Resolution](#element-id-resolution).

#### Enabling Click Autocapture

Pass an `AutocaptureOptions` instance through `MixpanelOptions` at initialization. Passing none leaves autocapture entirely uninitialized.

```swift Swift theme={null}
let options = MixpanelOptions(
    token: "YOUR_TOKEN",
    autocaptureOptions: AutocaptureOptions()
)

let mixpanel = Mixpanel.initialize(options: options)
```

Each signal is configurable, and can be turned off independently:

| Option             | Setting                                                                      | Default |
| ------------------ | ---------------------------------------------------------------------------- | ------- |
| `ClickOptions`     | `enabled`                                                                    | `true`  |
| `RageClickOptions` | `enabled`                                                                    | `true`  |
| `RageClickOptions` | `clickThreshold` — taps required                                             | `4`     |
| `RageClickOptions` | `timeWindowMs` — time window they must fall in, in milliseconds              | `1000`  |
| `RageClickOptions` | `radius` — spatial threshold, in points                                      | `44`    |
| `DeadClickOptions` | `enabled`                                                                    | `true`  |
| `DeadClickOptions` | `timeWindowMs` — time to wait before declaring a click dead, in milliseconds | `500`   |

```swift Swift theme={null}
let autocaptureOptions = AutocaptureOptions(
    rageClickOptions: RageClickOptions(clickThreshold: 5, timeWindowMs: 800),
    deadClickOptions: DeadClickOptions(enabled: false)
)

let options = MixpanelOptions(
    token: "YOUR_TOKEN",
    autocaptureOptions: autocaptureOptions
)
```

Autocapture is iOS-only — not available on Mac Catalyst, tvOS, watchOS or visionOS — and does not run inside app extensions.

#### Disabling Click Autocapture

Autocapture is a decision made at initialization: omit `autocaptureOptions` and none of the machinery is constructed. To turn it off in an app that already ships with it enabled, either stop passing the options, or disable each signal:

```swift Swift theme={null}
let autocaptureOptions = AutocaptureOptions(
    clickOptions: ClickOptions(enabled: false),
    rageClickOptions: RageClickOptions(enabled: false),
    deadClickOptions: DeadClickOptions(enabled: false)
)
```

There is no runtime toggle. Autocapture starts or doesn't at initialization, and it respects opt-out: it does not start when tracking is opted out, and stops and restarts with `optOutTracking()` and `optInTracking()`.

#### Tracked Properties

Every click, dead click and rage click event carries:

| Property          | Description                                                              |
| ----------------- | ------------------------------------------------------------------------ |
| `$el_id`          | Element identifier — see [Element ID Resolution](#element-id-resolution) |
| `$el_tag_name`    | Class name of the element, e.g. `UIButton`                               |
| `$attr-role`      | Element role, e.g. `Button`, `Switch`                                    |
| `$elements`       | Ancestor hierarchy, up to 5 levels                                       |
| `$x`, `$y`        | Touch coordinates, in screen points                                      |
| `$mp_autocapture` | Always `true`                                                            |

<Note>
  Autocapture does not capture element text: there is no `$el_text` property. There is also no `$attr-aria-label` property. `accessibilityLabel` is accessibility metadata — surfaced to assistive technologies such as VoiceOver rather than shown on screen — and it can carry personal data, so the SDK neither reports it nor uses it as an identifier.
</Note>

#### Element ID Resolution

`$el_id` is resolved by the SDK using fixed rules — it is not configurable. Understanding the order is the key to getting readable IDs in your reports.

**UIKit:**

1. **`accessibilityIdentifier`** — framework-internal values such as `_private` or `AXID-1` are skipped
2. **`<ClassName>_<hash>`** — the anonymous fallback

**SwiftUI:**

1. **`.accessibilityIdentifier("...")`** — read from SwiftUI's accessibility element tree
2. **`<ClassName>_<hash>`** — the anonymous fallback

<Warning>
  **SwiftUI identifiers need iOS 18+.** SwiftUI never sets `accessibilityIdentifier` on the backing UIKit view — it lives in the accessibility element tree, which the SDK reads with `accessibilityHitTest`. That query is available on iOS 18 and later, and returns nothing when the tree has not been materialized. On iOS 15–17, SwiftUI elements fall back to `<ClassName>_<hash>` regardless of the identifier you set. UIKit views are unaffected.
</Warning>

**About the hash fallback:** the hash is derived from the element's *position* in the hierarchy, not its instance, so it is identical across app launches. That makes it groupable, but it describes a position rather than a thing: reordering siblings changes it, and two rows of the same list differ by index rather than by content.

<Warning>
  **Review your identifiers for personal data.** `$el_id` is derived from identifiers you set in your own code, so its contents are under your control — and Mixpanel receives them as-is. An identifier built from user data (`user_4172@example.com_row`, an account number, an order ID) sends that data to Mixpanel as an event property. Before you rely on click autocapture in production, tap through your key flows with SDK logging enabled, review the `$el_id` values the SDK reports, and confirm none of them carry personally identifiable information.
</Warning>

#### Best Practices

* **Give every element you want to measure a stable `accessibilityIdentifier`.** `button.accessibilityIdentifier = "checkout_button"` in UIKit, `.accessibilityIdentifier("checkout_button")` in SwiftUI. Without one, the element reports a positional hash — groupable, but unreadable in reports, and it changes if you reorder siblings.
* **Put the identifier on the element that handles the tap**, not on a wrapper around it. The walk-up finds the nearest *interactive* ancestor and reads identity from there, so an identifier on a non-interactive container is never used.
* **Keep identifiers static across renders.** One built from a row index, a timestamp or a product name produces a new `$el_id` per render, which scatters one control across many rows in your reports.
* **On SwiftUI below iOS 18, expect hashes.** If those versions matter to your reports, host the control in an identifiable UIKit view, or track those flows manually with `trackClick`.
* **Verify — and review for PII — before you rely on the data.** Set `loggingEnabled = true`, tap through your key flows, and confirm the reported `$el_id` values are the ones you expect *and* that none of them contain personal data. Identifiers are yours to choose, so keeping PII out of them is yours to confirm.

## Session Replay

Install the [Session Replay SDK for Swift](/docs/tracking-methods/sdks/swift/swift-replay) to record replay data. Learn more about [Session Replay](/docs/session-replay) and [implementing Session Replay on iOS](/docs/tracking-methods/sdks/swift/swift-replay).

## Debugging and Logging

### Enable SDK logs

Enable debug mode by setting the [`loggingEnabled`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC14loggingEnabledSbvp) configuration to `true`.

**Example Usage**

```swift Swift theme={null}
/// enable debug log
Mixpanel.mainInstance().loggingEnabled = true
```

Learn more about [debugging](/docs/tracking-best-practices/debugging).

### Network Failover

Configure a backup host for automatic failover when the primary API endpoint is unreachable. The default primary endpoint is `api.mixpanel.com` (or your configured custom endpoint for EU/India data residency or proxy server). This can be used as a troubleshooting fallback when network calls to the Mixpanel endpoint are blocked or unreachable.

**Setup**

```swift Swift theme={null}
let mixpanel = Mixpanel.initialize(token: "YOUR_PROJECT_TOKEN")
mixpanel.backupHost = "api-backup.mixpanel.com"
```

The SDK automatically retries failed requests to the backup host, preserving the same URL path, authentication, and headers as the primary.

**How It Works**

Failover triggers:

* Network failures (connection timeout, DNS errors)
* Non 4xx HTTP status codes

No failover for:

* Client errors (HTTP 4xx) - indicates invalid request
* Successful responses (HTTP 200 OK)
* Parse errors

This works with proxy server setups as well - if the proxy fails, the backup will be tried.

**Requirements**

Your backup host must:

* Support the same API endpoints (`/track/`, `/engage/`, `/groups/`)
* Accept the same authentication mechanism as the primary host
* Use the same URL scheme and port as configured for the primary host

## Privacy-Friendly Tracking

You have control over the data you send to Mixpanel. The Swift SDK provides methods to help you protect user data.

Learn more about [Privacy](/docs/privacy/overview).

### Opt Out of Tracking

The Swift SDK is initialized with tracking enabled by default. Use the [`optOutTracking()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC14optOutTrackingyyF) method to opt the user out of data tracking and local storage for the current Mixpanel instance.

**Example Usage**

```swift Swift theme={null}
/// send "some_event"
Mixpanel.mainInstance().track(event: "some_event")

/// opt user out of tracking
/// SDK is prevented from sending any data
Mixpanel.mainInstance().optOutTracking()

/// this track call will not work
Mixpanel.mainInstance().track(event: "some_event")
```

#### Opt Out by Default

For GDPR compliance, you can initialize the SDK with users opted out of tracking by default using the [`optOutTrackingByDefault`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) option (see [Configuration Reference](#configuration-reference)). Once the user consents to tracking, call [`optInTracking()`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelInstance.html#/s:8Mixpanel0A8InstanceC13optInTracking10distinctId10propertiesySSSg_SDySSAA0A4Type_pGSgtF) to start tracking.

**Example Usage**

```swift Swift theme={null}
/// create Mixpanel instance with users opted out of tracking by default
/// SDK is prevented from sending data
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    optOutTrackingByDefault: true
)
Mixpanel.initialize(options: options)

/// this track call will not work
Mixpanel.mainInstance().track(event: "some_event")

/// opt user into tracking
Mixpanel.mainInstance().optInTracking()

/// send "some_event"
Mixpanel.mainInstance().track(event: "some_event")
```

### Data Residency

<Note>
  Setting `serverURL` during initialization ensures all requests (events, profiles, and feature flags) use the correct endpoint from the start.
</Note>

#### EU Data Residency

Route data to Mixpanel's EU servers by setting the [`serverURL`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) option to `https://api-eu.mixpanel.com` during initialization (see [Configuration Reference](#configuration-reference)).

**Example Usage**

```swift Swift theme={null}
/// Initialize Mixpanel with EU data residency
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    serverURL: "https://api-eu.mixpanel.com"
)
Mixpanel.initialize(options: options)
```

#### India Data Residency

Route data to Mixpanel's India servers by setting the [`serverURL`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) option to `https://api-in.mixpanel.com` during initialization (see [Configuration Reference](#configuration-reference)).

**Example Usage**

```swift Swift theme={null}
/// Initialize Mixpanel with India data residency
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    serverURL: "https://api-in.mixpanel.com"
)
Mixpanel.initialize(options: options)
```

### Disable Geolocation

The Swift SDK parses the request IP address to generate geolocation properties for events and profiles. To disable geolocation and prevent IP-based tracking, set [`useIPAddressForGeoLocation`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) to `false` (see [Configuration Reference](#configuration-reference)).

You can configure this during initialization or change it after:

**Recommended: Configure During Initialization**

```swift Swift theme={null}
let options = MixpanelOptions(
    token: "YOUR_PROJECT_TOKEN",
    useIPAddressForGeoLocation: false
)
Mixpanel.initialize(options: options)
```

**Alternative: Update After Initialization**

```swift Swift theme={null}
/// Disable IP-based geolocation at runtime
Mixpanel.mainInstance().useIPAddressForGeoLocation = false
```

## Legacy Automatically Tracked Events

Mixpanel's SDKs have a legacy feature to automatically collect common mobile events. **We don't recommend enabling this**, as these events rely on client-side state and can be unreliable.

The [`trackAutomaticEvents`](https://mixpanel.github.io/mixpanel-swift/Classes/MixpanelOptions.html) option is set to `false` by default (see [Configuration Reference](#configuration-reference)). You only need to explicitly set this if you want to enable automatic event tracking.

See below table for automatic events tracked by the Swift SDK when `trackAutomaticEvents` is enabled:

| Raw Name          | Display Name          | Description                                                                                                                                                                                                                                                                                                                                           |
| ----------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| \$ae\_first\_open | First App Open        | Tracks the first time the user has opened the app. This event is retriggered if the user reinstalls the app or clears local storage. A user property First App Open Date (`$ae_first_app_open_date`) is tracked to indicate the date when app was first opened.                                                                                       |
| \$ae\_updated     | App Updated           | Executes when a user updates the app from the previous version. A Version Updated (`$ae_updated_version`) property is tracked to store the new app version.                                                                                                                                                                                           |
| \$ae\_session     | App Session           | Executes when a user spends more than 10 seconds in the app. A Session Length (`$ae_session_length`) property is tracked to reflect the number of seconds user spent in the session. In addition, there are two user properties tracked: Total App Sessions (`$ae_total_app_sessions`) and Total App Session Length (`$ae_total_app_session_length`). |
| \$ae\_iap         | In App Purchase (IAP) | Executes when a user conducts an in-app purchase through your app. Mixpanel provides three properties for this event: Product Name (`$ae_iap_name`), Product Quantity (`$ae_iap_quantity`), and Product Price (`$ae_iap_price`).                                                                                                                      |

## Release History

[See All Releases](https://github.com/mixpanel/mixpanel-swift/releases).
