---
title: Instrument and configure the iOS Swift SDK
description: "Track events, manage identity and sessions, configure Autocapture, control data delivery, and add plugins with the Amplitude iOS Swift SDK."
product: analytics
lang: en
last_updated: 2026-11-05
token_estimate: 13530
---
# Instrument and configure the iOS Swift SDK

After you [get started](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/get-started), use the iOS Swift SDK (`AmplitudeSwift`) to track your own events, manage identity and sessions, choose Autocapture features, control how events reach Amplitude, and extend the SDK with plugins. For exact signatures and every option, go to the [Reference](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/reference). To find the release that added or changed a feature, go to [iOS Swift SDK release notes](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk-release-notes).

> For AI agents: a documentation index is available at [/docs/llms.txt](/docs/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

The samples use Swift. For the Objective-C names of each class and method, go to [Objective-C](#objective-c).

## Track events

The iOS Swift SDK sends five kinds of data: custom events, user properties, group membership, group properties, and revenue. Each has its own method on the `Amplitude` instance. Every method returns immediately. The SDK does the work on a background queue, then stores the event and uploads it in a batch. Use these methods for events your tracking plan defines. For sessions, app lifecycle events, screen views, and other events the SDK can collect without code, go to [Autocapture](#autocapture).

### Track a custom event

Pass an event name and, optionally, a dictionary of event properties.

```swift
amplitude.track(
    eventType: "Song Played",
    eventProperties: [
        "title": "Here Comes the Sun",
        "duration_seconds": 185,
        "genres": ["rock", "pop"],
    ]
)
```

Use strings, numbers, booleans, arrays, or nested dictionaries as property values. The SDK doesn't set `app_version` for you. To send it, set `appVersion` on a `BaseEvent`, or add it to every event with a [plugin](#add-a-plugin).

#### Pass a BaseEvent

To set more fields than the name and properties, pass a `BaseEvent`. Its initializer accepts every field the HTTP V2 API accepts, such as `timestamp`, `insertId`, `appVersion`, and `groups`. Swift requires labeled arguments in declaration order, so `eventType` comes after the event options fields.

```swift
let event = BaseEvent(
    timestamp: Int64(Date().timeIntervalSince1970 * 1000) - 60_000,
    insertId: "song-played-8f2c",
    appVersion: "4.2.0",
    eventType: "Song Played",
    eventProperties: ["title": "Here Comes the Sun"]
)
amplitude.track(event: event)
```

Set `insertId` when the same event might send twice, for example after a retry from your own code. Amplitude ignores a second event with the same `insert_id` and `device_id` within seven days. The SDK generates an `insertId` for every event that doesn't have one.

#### Override fields for one event

Pass an `EventOptions` object in `options`. The values apply to this event only. Later events still use the identity from `setUserId()` and `setDeviceId()`.

```swift
amplitude.track(
    eventType: "Invite Sent",
    eventProperties: ["channel": "email"],
    options: EventOptions(userId: "user-12345")
)
```

`identify()` behaves differently. When its `options` include `userId` or `deviceId`, the SDK also changes its identity to those values, so every later event carries them.

#### Get the upload result for one event

Pass a closure to `track(event:options:callback:)`. The SDK calls the closure after Amplitude accepts or rejects the event. The SDK doesn't call it while the upload is still retrying.

```swift
amplitude.track(event: BaseEvent(eventType: "Checkout Started")) { event, code, message in
    print("\(event.eventType): \(code) \(message)")
}
```

A successful upload reports code `200` and the message `Successfully send event`. The SDK keeps per-event closures in memory only, so the SDK loses a closure if the app quits before the upload. To receive the result of every event, set the [`callback`](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/reference#callback) configuration option. When you set both, both fire.

### Set user properties

User properties describe the user and stay attached to every later event until they change. Build an `Identify` object, add one or more operations, and pass it to `identify(identify:)`.

```swift
let identify = Identify()
    .set(property: "plan", value: "pro")
    .setOnce(property: "first_seen_at", value: "2026-10-26")
    .add(property: "login_count", value: 1)
    .append(property: "visited_sections", value: "billing")

amplitude.identify(identify: identify)
```

The SDK sends an `$identify` event. It doesn't appear as an event in charts, but it updates the user's properties. The update shows on the user's profile right away, and it applies to the events the user sends after it.

| Method | Effect on the user property |
| --- | --- |
| `set(property:value:)` | Sets the value, replacing any existing value. |
| `setOnce(property:value:)` | Sets the value only if the property has no value yet. Use it for first-touch facts, such as a signup date. |
| `add(property:value:)` | Adds the number to the current numeric value. Accepts `Int`, `Int64`, `Float`, and `Double`. Pass a negative number to subtract. |
| `append(property:value:)` | Adds the value, or each item of an array, to the end of a list property. Creates the list if it doesn't exist. |
| `prepend(property:value:)` | Adds the value, or each item of an array, to the start of a list property. |
| `postInsert(property:value:)` | Like `append`, but skips values the list already contains. |
| `preInsert(property:value:)` | Like `prepend`, but skips values the list already contains. |
| `remove(property:value:)` | Removes the value, or each item of an array, from a list property. |
| `unset(property:)` | Removes the property from the user. |
| `clearAll()` | Removes every user property. You can't undo it. Ignores every other operation in the same `Identify` object. |

Each property can appear in only one operation per `Identify` object. The SDK ignores a second operation on the same property and logs `Already used property <name> in previous operation, ignoring operation <operation>`. Split conflicting operations across two `identify()` calls.

To set a few properties without building an `Identify` object, pass a dictionary. The SDK sends each key as a `$set` operation:

```swift
amplitude.identify(userProperties: ["plan": "pro", "theme": "dark"])
```

#### Identify calls that only set properties wait for the next event

When an `$identify` contains only `$set` operations, the SDK holds it instead of uploading it at once. It merges every held identify into one `$identify` and sends it when one of these happens first:

- You track an event other than an identify or group identify. The merged identify goes ahead of that event.
- You send an identify with any other operation. The merged identify goes ahead of it.
- An event arrives with a different user ID or device ID than the held identify.
- You call `flush()`.
- 30 seconds pass.

An identify with any other operation goes into the upload queue right away. A `clearAll()` identify also discards every held identify. The `identifyBatchIntervalMillis` option has no effect as of 1.19.4: the SDK always waits 30 seconds.

### Assign a user to a group

Groups let you analyze behavior at the account or organization level. Group analytics requires the Accounts add-on.

`setGroup(groupType:groupName:)` assigns the current user to one group, or to several groups of the same type when you pass an array. The SDK sends an `$identify` event with `groups` set, and also sets the group type as a user property, replacing any earlier value.

```swift
amplitude.setGroup(groupType: "orgId", groupName: "15")
amplitude.setGroup(groupType: "sport", groupName: ["tennis", "soccer"])
```

To associate only one event with a group, without changing the user's groups, set `groups` on the event:

```swift
amplitude.track(event: BaseEvent(
    eventType: "Report Exported",
    groups: ["orgId": "15"]
))
```

### Set group properties

Group properties describe the group, not the user. Use `groupIdentify(groupType:groupName:identify:)` with an `Identify` object. All `Identify` operations work the same way as they do for user properties. Updates apply only to events from that point onward.

```swift
let groupProperties = Identify()
    .set(property: "plan", value: "enterprise")
    .set(property: "seats", value: 250)

amplitude.groupIdentify(groupType: "orgId", groupName: "15", identify: groupProperties)
```

### Track revenue

Build a `Revenue` object and pass it to `revenue(revenue:)`. The SDK sends a `revenue_amount` event that Amplitude uses for revenue analysis.

```swift
let revenue = Revenue()
revenue.productId = "com.example.pro_annual"
revenue.price = 3.99
revenue.quantity = 3
revenue.revenueType = "subscription"
revenue.currency = "USD"
revenue.properties = ["coupon": "FALL26"]
revenue.setReceipt(receipt: receiptData, receiptSignature: receiptSignature)

amplitude.revenue(revenue: revenue)
```

`price` is required. Without it, the SDK drops the call and logs `Invalid revenue object, missing required fields`. Amplitude computes revenue as `price * quantity` unless you set `revenue`, which sets the total directly. `quantity` defaults to `1` and ignores values of `0` or less. `price` accepts negative values, for example for a refund. Use the App Store product identifier as `productId`. Revenue verification requires `revenueType` and the receipt signature. For every property, go to [Revenue class](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/reference#revenue-class).

Amplitude converts revenue to your project's currency only when you set `currency`. For more on currency conversion and revenue event setup, go to [Track revenue](https://amplitude.com/docs/data/sources/instrument-track-revenue).

`logRevenue()` is deprecated and has no effect as of 1.19.4. It doesn't send an event. Use `revenue(revenue:)`.

> **Tip:**
>
> To get the most from revenue data, also track products with the [product array](https://amplitude.com/docs/analytics/charts/cart-analysis) method.

### Track deep links

The SDK doesn't capture deep links automatically. When your app opens a URL, track a `DeepLinkOpenedEvent`. The SDK sends `[Amplitude] Deep Link Opened` with `[Amplitude] Link URL` and, when you pass one, `[Amplitude] Link Referrer`.

```swift
// From a URL, for example in onOpenURL or scene(_:openURLContexts:)
amplitude.track(event: DeepLinkOpenedEvent(url: url))

// From strings, with a referrer
amplitude.track(event: DeepLinkOpenedEvent(
    url: "https://www.example.com/products/42",
    referrer: "https://www.google.com"
))

// From a universal link, which reads webpageURL and referrerURL
amplitude.track(event: DeepLinkOpenedEvent(activity: userActivity))
```

### Send events to more than one project

Each Amplitude project has its own API key. To send to two projects, create a second `Amplitude` instance with its own API key and a distinct `instanceName`.

```swift
let production = Amplitude(configuration: Configuration(
    apiKey: "PRODUCTION_API_KEY"
))
let marketing = Amplitude(configuration: Configuration(
    apiKey: "MARKETING_API_KEY",
    instanceName: "marketing"
))

production.track(eventType: "Signup Completed")
marketing.track(eventType: "Signup Completed")
```

Each instance keeps its own user ID, device ID, session, event queue, and plugins. The SDK scopes storage by API key and instance name. Keep a strong reference to each instance for the life of your app.

## Identity and sessions

The iOS Swift SDK attaches three identifiers to every event: a device ID, an optional user ID, and a session ID. Amplitude uses them to merge events into users and sessions. The SDK persists the device ID, user ID, and session state in `UserDefaults`, scoped to the API key and instance name, so they survive app launches.

### How the SDK chooses identifiers on launch

When you create the `Amplitude` instance, the SDK takes each identifier from the first source that has a value.

#### Device ID

1. The `deviceId` configuration option. Requires 1.19.0 or later.
2. The device ID stored from an earlier launch.
3. The identifier for vendor (IDFV), unless you turned it off with `trackingOptions.disableTrackIDFV()`.
4. A new random UUID.

On macOS, the SDK uses the MAC address of the `en0` network interface, in the form `xx:xx:xx:xx:xx:xx`, as the IDFV. On macOS 27 and later, the system reports the placeholder `02:00:00:00:00:00` for every Mac, so the SDK skips it and uses a UUID. On watchOS, the SDK uses `WKInterfaceDevice.identifierForVendor`.

The SDK replaces a stored device ID that's a known shared value, such as an all-zero UUID or the macOS placeholder address. It keeps a configured `deviceId` even then, and logs a warning. The SDK doesn't collect the advertising identifier (IDFA). For attribution, set `idfa` on events yourself from a [plugin](#add-a-plugin), after the user grants App Tracking Transparency permission. For an example, go to [IDFACollectionPlugin.swift](https://github.com/amplitude/Amplitude-Swift/blob/v1.19.4/Examples/AmplitudeSwiftUIExample/AmplitudeSwiftUIExample/ExamplePlugins/IDFACollectionPlugin.swift) in the SDK repository.

#### User ID

1. The `userId` configuration option. Requires 1.19.0 or later.
2. The user ID stored from an earlier launch.

A configured value replaces the stored one, and the SDK stores it for later launches. With `nil`, the SDK keeps the stored value.

#### Session ID

The session ID is the start time of the session, in milliseconds since the Unix epoch. The SDK reads the last session ID and last event time from storage, then decides with the first event whether the session continues. Go to [How sessions work](#how-sessions-work).

### Set the user ID

Call `setUserId(userId:)` when a user signs in. Use a stable, unique identifier from your system, such as a database ID. Avoid email addresses if they can change.

```swift
amplitude.setUserId(userId: "user-12345")
```

The new user ID applies to every later event and persists across launches. User IDs and device IDs must be at least five characters, or Amplitude rejects the event. To allow shorter IDs, set `minIdLength`. To read the current value, call `getUserId()`.

If you know the user ID at launch, pass it to the configuration instead, so the first session event and any lifecycle events carry it. The `userId` configuration option requires 1.19.0 or later.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    userId: "user-12345"
))
```

When one user signs in on several devices, set the same user ID on each. Amplitude merges the devices into one user.

### Reset when a user logs out

Call `reset()` when a user logs out. It clears the user ID and the user properties the SDK tracks locally, then chooses a device ID again, so later activity doesn't merge into the signed-out user.

```swift
amplitude.reset()
```

`reset()` chooses the device ID the same way the SDK does on a first launch. With IDFV tracking on, which is the default on iOS, the new device ID is the same IDFV, so Amplitude can still link the two users through the device. To get a new random device ID on every reset, turn off IDFV tracking:

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    trackingOptions: TrackingOptions().disableTrackIDFV()
))
```

`reset()` doesn't start a new session, send a `$clearAll` to Amplitude, change opt-out, or remove queued events. The SDK notifies plugins that implement `onReset()`.

### Set the device ID

The SDK chooses a device ID on first launch. Set your own only when you already have a stable anonymous identifier, for example one shared with your server.

```swift
amplitude.setDeviceId(deviceId: UUID().uuidString)
```

Use a value that's unique per device and at least five characters long. Two devices with the same device ID merge into one user in Amplitude. The SDK stores the device ID in `UserDefaults`, which device backups include, so restoring a backup on a new device can carry the old device ID with it. To read the current value, call `getDeviceId()`.

### How sessions work

A session groups the events a user triggers in one visit. Its ID is the Unix timestamp, in milliseconds, of the session's first event.

- While the app is in the foreground, the session never times out, no matter how long the user stays.
- When the app moves to the background, the SDK records the time. If the app returns to the foreground within `minTimeBetweenSessionsMillis` (default 5 minutes), the session continues. Later, a new session starts.
- An event tracked while the app is in the background continues the session if it comes within `minTimeBetweenSessionsMillis` of the previous event. Otherwise, it starts a new session. Background events therefore lengthen a session.
- With session autocapture on (the default), the SDK sends `session_start` at the start of each session and `session_end` at the end of the previous one. Amplitude shows them as `[Amplitude] Start Session` and `[Amplitude] End Session`. The SDK sends `session_end` when the next session starts, not when the user leaves, so a user who never returns has no `session_end`.
- With session autocapture off, events still carry a session ID. Only the `session_start` and `session_end` events stop.
- On macOS, the SDK treats the app as in the background whenever it isn't the active app, for example when the user switches to another app.

Session events carry the current user ID and device ID, but not user properties. A user property set during a session shows on later events, not on the earlier `session_start`.

To change the timeout, set `minTimeBetweenSessionsMillis`:

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    minTimeBetweenSessionsMillis: 15 * 60 * 1000 // 15 minutes
))
```

To read the current session ID, call `getSessionId()`. For how Amplitude counts session length and session metrics, go to [Track sessions](https://amplitude.com/docs/data/sources/instrument-track-sessions).

#### Track an event outside any session

Set `sessionId` to `-1` in the event options. The event doesn't belong to a session, doesn't extend the current one, and doesn't start a new one. Use it for activity the user didn't start, such as a received push notification.

```swift
amplitude.track(
    eventType: "Push Notification Received",
    options: EventOptions(sessionId: -1)
)
```

The same option works with `identify(identify:options:)`.

#### Start or end a session yourself

`setSessionId(timestamp:)` ends the current session and starts one with the ID you pass, in milliseconds since the Unix epoch. `setSessionId(date:)` does the same with a `Date`. A negative timestamp ends the current session without starting a new one. The next event then starts a session.

```swift
amplitude.setSessionId(date: Date())
```

The SDK treats the ID as the session's last activity time. If the app is in the background and you pass an ID older than `minTimeBetweenSessionsMillis`, the next event starts a new session instead of joining yours.

## Autocapture

Autocapture is the set of events the iOS Swift SDK can track without code. You choose features with the `autocapture` option, and your Amplitude project's remote configuration can turn features on or off. For a general introduction, go to [Autocapture](https://amplitude.com/docs/data/autocapture).

### Choose features

`autocapture` takes an `AutocaptureOptions` option set. The default is `.sessions`.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    autocapture: [.sessions, .appLifecycles, .screenViews]
))
```

Pass `.all` for every feature, or `[]` to turn off Autocapture, including session events. Set `autocapture` when you create the configuration. You can't change it on the instance afterward.

| Feature | Option | Default | Platforms | Events |
| --- | --- | --- | --- | --- |
| [Sessions](#how-sessions-work) | `.sessions` | On | All | `session_start`, `session_end` |
| [App lifecycles](#app-lifecycles) | `.appLifecycles` | Off | UIKit platforms | `[Amplitude] Application Installed`, `[Amplitude] Application Updated`, `[Amplitude] Application Opened`, `[Amplitude] Application Backgrounded` |
| [Screen views](#screen-views) | `.screenViews` | Off | UIKit platforms | `[Amplitude] Screen Viewed` |
| [Element interactions](#element-interactions) | `.elementInteractions` | Off | UIKit platforms | `[Amplitude] Element Interacted` |
| [Frustration interactions](#frustration-interactions) | `.frustrationInteractions` | Off | UIKit platforms | `[Amplitude] Rage Click`, `[Amplitude] Dead Click` |
| [Network requests](#network-requests) | `.networkTracking` | Off | All except watchOS | `[Amplitude] Network Request` |

UIKit platforms are iOS, iPadOS, tvOS, visionOS, and Mac Catalyst. On macOS and watchOS, only sessions work, plus network requests on macOS. The SDK has no SwiftUI-native capture: in a SwiftUI app, element interactions and frustration interactions skip SwiftUI views, and screen views see only UIKit view controllers. For SwiftUI screens, [track screen views yourself](#screen-views). If you build with the `AMPLITUDE_DISABLE_UIKIT` flag, the UIKit features are off on every platform.

`autocapture` replaces the deprecated `defaultTracking` option. Setting `defaultTracking` overwrites `autocapture` with its sessions, app lifecycles, and screen views values, and turns off every other feature.

### Remote configuration overrides local settings

With `enableAutoCaptureRemoteConfig` on (the default), the SDK reads your project's Autocapture settings from Amplitude and applies them on top of your `autocapture` value. For each feature that remote configuration sets, its value replaces yours, in either direction: it can turn on a feature you left off, or turn off one you turned on. Features it doesn't set keep your local value. Remote configuration can also replace your network tracking rules and turn rage clicks and dead clicks on or off. Changes apply to events tracked after the SDK receives them, without an app update. Requires 1.13.0 or later.

To use only your local settings, turn off `enableAutoCaptureRemoteConfig`. The option is read-only after you create the configuration.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    autocapture: [.sessions, .appLifecycles],
    enableAutoCaptureRemoteConfig: false
))
```

### App lifecycles

With `.appLifecycles`, the SDK observes `UIApplication` notifications and sends:

| Event | When | Properties |
| --- | --- | --- |
| `[Amplitude] Application Installed` | The first launch after install. | `[Amplitude] Version`, `[Amplitude] Build` |
| `[Amplitude] Application Updated` | The first launch after the version or build changes. | `[Amplitude] Version`, `[Amplitude] Build`, `[Amplitude] Previous Version`, `[Amplitude] Previous Build` |
| `[Amplitude] Application Opened` | Every launch, and every return to the foreground. | `[Amplitude] Version`, `[Amplitude] Build`, `[Amplitude] From Background` |
| `[Amplitude] Application Backgrounded` | The app enters the background. | None |

The version is `CFBundleShortVersionString` and the build is `CFBundleVersion`.

### Screen views

With `.screenViews`, the SDK sends `[Amplitude] Screen Viewed` when a view controller from your app's main bundle appears as the top view controller. It swizzles `UIViewController.viewDidAppear(_:)` to detect this. `[Amplitude] Screen Name` is the view controller's `title`, or its class name without the `ViewController` suffix. The SDK skips a view controller that's already the last one it reported.

In SwiftUI, track screens yourself, for example from `onAppear`:

```swift
amplitude.track(event: ScreenViewedEvent(screenName: "Settings"))
```

### Element interactions

With `.elementInteractions`, the SDK sends `[Amplitude] Element Interacted` when a user interacts with a `UIControl` or a `UIGestureRecognizer`. It swizzles `UIApplication.sendAction(_:to:from:for:)` and the gesture recognizer `state` setter. It skips SwiftUI views and pan and pinch gestures on scroll views.

| Property | Value |
| --- | --- |
| `[Amplitude] Action` | For controls: `touch`, `edit`, `valueChange`, `primaryAction`, `menuAction`, or `didEndEditing` for text fields. For gestures: `tap`, `swipe`, `pan`, `longPress`, `pinch`, `rotation`, `screenEdgePan`, or the recognizer class name. |
| `[Amplitude] Target View Class` | The class of the view the user interacted with. |
| `[Amplitude] Target Text` | The title of the target control. |
| `[Amplitude] Target Accessibility Label` | The view's accessibility label. |
| `[Amplitude] Target Accessibility Identifier` | The view's accessibility identifier. |
| `[Amplitude] Action Method` | The selector the control sends. |
| `[Amplitude] Gesture Recognizer` | The gesture recognizer class, for gestures. |
| `[Amplitude] Hierarchy` | The view's class hierarchy, from the most specific class to the most general. |
| `[Amplitude] Screen Name` | The current screen, named the same way as [screen views](#screen-views). |

### Frustration interactions

With `.frustrationInteractions`, the SDK detects rage clicks and dead clicks on UIKit controls and gesture recognizers. Requires 1.14.0 or later.

- **Rage click**: four or more taps on the same element within one second, each within 50 points of the previous one. The SDK sends `[Amplitude] Rage Click` with the element properties above, plus `[Amplitude] Begin Time`, `[Amplitude] End Time` (ISO 8601), `[Amplitude] Duration` (milliseconds), `[Amplitude] Click Count`, and `[Amplitude] Clicks`, an array of `X`, `Y`, and `Time` for each tap. Coordinates start at the top left of the screen.
- **Dead click**: a tap on an interactive element followed by no visible change within three seconds. The SDK sends `[Amplitude] Dead Click` with the element properties, plus `[Amplitude] X` and `[Amplitude] Y`. Taps inside a `WKWebView` don't count.

Dead clicks need a plugin that reports interface changes to the SDK, such as the [Session Replay iOS plugin](https://amplitude.com/docs/sdks/session-replay/session-replay-ios-plugin), installed and recording. Without one, the SDK never sends `[Amplitude] Dead Click`.

To turn off one kind, set `interactionsOptions`:

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    autocapture: [.sessions, .frustrationInteractions],
    interactionsOptions: InteractionsOptions(
        rageClick: RageClickOptions(enabled: true),
        deadClick: DeadClickOptions(enabled: false)
    )
))
```

To exclude a view and its subviews, call `amp_ignoreInteractionEvent(rageClick:deadClick:)` on it. Both parameters default to `true`.

```swift
canvasView.amp_ignoreInteractionEvent()
carouselView.amp_ignoreInteractionEvent(rageClick: true, deadClick: false)
```

### Network requests

With `.networkTracking`, the SDK sends `[Amplitude] Network Request` for requests your app makes with `URLSession`. It swizzles `URLSessionTask` methods to observe them. It doesn't see `NSURLConnection` requests, and it doesn't run on watchOS. Requires 1.12.0 or later.

By default, the SDK captures requests to every host except `*.amplitude.com` that end with a status code from `500` through `599`. To change that, set `networkTrackingOptions`:

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    autocapture: [.sessions, .networkTracking],
    networkTrackingOptions: NetworkTrackingOptions(
        captureRules: [
            .init(hosts: ["*"], statusCodeRange: "400-599"),
            .init(hosts: ["api.example.com"], statusCodeRange: "0,500-599"),
        ],
        ignoreHosts: ["*.notmyapi.com"]
    )
))
```

For each request, the SDK checks `ignoreHosts` first, then uses the last rule in `captureRules` whose hosts, URLs, and methods match. Only that rule's `statusCodeRange` decides whether the SDK captures the request. With the configuration above:

- `api.example.com` returns `503`: captured, by the second rule.
- `api.example.com` returns `401`: not captured. The second rule matches the host, and `401` isn't in its range.
- `cdn.example.net` returns `401`: captured, by the first rule.
- `ads.notmyapi.com` returns `500`: not captured, because the host is ignored.

Status code `0` means the request failed before a response, for example with no connection. Host patterns accept `*` wildcards and ignore case. Put rules for specific hosts after general ones.

The event includes these properties:

| Property | Value |
| --- | --- |
| `[Amplitude] URL` | The URL without query or fragment. The SDK replaces a user name and password with `[mask]`. |
| `[Amplitude] URL Query`, `[Amplitude] URL Fragment` | The query and fragment. |
| `[Amplitude] Request Method` | The HTTP method. |
| `[Amplitude] Status Code` | The HTTP status code. |
| `[Amplitude] Error Code`, `[Amplitude] Error Message` | The local error, when the request failed without a response. |
| `[Amplitude] Start Time`, `[Amplitude] Completion Time` | Milliseconds since the Unix epoch. |
| `[Amplitude] Duration` | Milliseconds. |
| `[Amplitude] Request Body Size`, `[Amplitude] Response Body Size` | Bytes. |
| `[Amplitude] Request Headers`, `[Amplitude] Response Headers` | Only when the matching rule sets `requestHeaders` or `responseHeaders`. |
| `[Amplitude] Request Body`, `[Amplitude] Response Body` | Only when the matching rule sets `requestBody` or `responseBody`. JSON bodies only. |

#### Capture headers and bodies

To capture headers and JSON bodies, create a rule with `urls`, which takes exact URLs or regular expressions. With `urls` set, the rule ignores `hosts`. URL rules and header and body capture require 1.15.0 or later.

```swift
let rule = NetworkTrackingOptions.CaptureRule(
    urls: [
        .exact("https://api.example.com/v1/orders"),
        .regex("^https://api\\.example\\.com/v1/users/.*"),
    ],
    methods: ["POST", "PUT"],
    statusCodeRange: "400-599",
    requestHeaders: .init(allowlist: ["X-Request-ID"]),
    responseHeaders: .init(allowlist: ["X-Trace-ID"]),
    requestBody: .init(allowlist: ["user/*", "product/**/id"]),
    responseBody: .init(allowlist: ["data/**"], excludelist: ["**/password"])
)

let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    autocapture: [.sessions, .networkTracking],
    networkTrackingOptions: NetworkTrackingOptions(captureRules: [.init(hosts: ["*"]), rule])
))
```

- Header allowlists ignore case. With `captureSafeHeaders` on (the default), the SDK also captures a fixed list of non-sensitive headers, such as `content-type` and `cache-control`. It never captures `authorization`, `cookie`, or `proxy-authorization`, even when you allow them.
- Body paths are case-sensitive. `*` matches one level and `**` matches any number of levels. `excludelist` removes paths that `allowlist` selected.
- The SDK can capture a response body only for data tasks created with a completion handler.

## Data delivery

The iOS Swift SDK doesn't send each event as it's tracked. It saves events to files on the device, uploads them in batches, and retries failed uploads. This section covers each setting that changes that behavior. The defaults suit most apps.

### How the SDK uploads events

1. The SDK writes each event to the current storage file in the app's Application Support directory (Caches on tvOS). The SDK excludes the directory from device backups.
2. An upload starts when the first of these happens:

   - `flushIntervalMillis` (default `30000`) passes. The timer repeats.
   - The number of events since the last upload reaches `flushQueueSize` (default `30`).
   - The app moves to the background, when `flushEventsOnClose` is `true` (the default).
   - You call `flush()`.
   - The device reconnects to a network.
3. The upload closes the current file and sends each stored file, oldest first, as one request. Only one request is in flight at a time. A file can hold more than `flushQueueSize` events, for example after a period offline.
4. The SDK handles the response:

   - **2xx**: deletes the file and reports success to callbacks.
   - **400 with an invalid API key**: drops every event in the file and reports `400` with the message `Invalid API key: <key>`.
   - **400 for specific events**: drops the events Amplitude lists as invalid or silenced, reports `400` for them, and queues the rest again.
   - **413**: splits the file in half and sends each half. The SDK drops a single event that's still too large and reports `413`.
   - **429, 408, 5xx, and other codes**: keeps the file and retries. Callbacks don't fire.
5. After each failed retry, the SDK waits 1, 2, 4, and then up to 60 seconds before the next attempt. After more than `flushMaxRetries` (default `6`) failures in a row, it sets `offline` to `true` and stops uploading. [Default changed in 1.11.9](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk-release-notes#1119). Uploads resume when the network status changes, when you set `offline` to `false`, or on the next launch. The events stay on the device until then.
6. A connection error, such as no internet connection, an unreachable host, or an App Transport Security block, sets `offline` to `true` right away.

The SDK asks iOS for background execution time while an upload runs, so a request can finish after the user leaves the app.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    flushQueueSize: 50,
    flushIntervalMillis: 10_000,
    flushMaxRetries: 10
))

// Upload the queue now, for example before a long-running task.
amplitude.flush()
```

`flushIntervalMillis` takes effect only when you create the instance. Changing it later has no effect.

### Tune upload performance

`track()` doesn't wait for the network, so most tuning is a tradeoff between request count and delay:

- **Fewer requests**: raise `flushIntervalMillis` or `flushQueueSize`. Events wait longer on the device before they upload.
- **Faster delivery**: lower `flushIntervalMillis` or `flushQueueSize`. The SDK sends more, smaller requests and uses more battery.
- **High-volume sources**: set `useBatch: true` to send to the [Batch Event Upload API](https://amplitude.com/docs/apis/analytics/batch-event-upload), which has higher rate limits. It doesn't change how the SDK batches events on the device.

To cap how many events the SDK keeps on the device, set `maxQueuedEventCount`. When you create the instance, the SDK deletes the oldest storage files until the newest files hold at least that many events. It deletes whole files and checks only at launch. More events than the limit can remain, especially after a long time offline, when every event is in one file. Requires 1.9.1 or later.

### Choose a data region

Set `serverZone` to the data center of your Amplitude organization. The default is `.US`.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    serverZone: .EU
))
```

| `serverZone` | `useBatch: false` | `useBatch: true` |
| --- | --- | --- |
| `.US` | `https://api2.amplitude.com/2/httpapi` | `https://api2.amplitude.com/batch` |
| `.EU` | `https://api.eu.amplitude.com/2/httpapi` | `https://api.eu.amplitude.com/batch` |

`serverZone` also selects the region for remote configuration and SDK diagnostics. Set it when you create the configuration.

### Send events through a proxy

Use a proxy on your own domain to route events through infrastructure you control. Point `serverUrl` at the proxy, and forward each request body unchanged to the Amplitude endpoint for your region.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    serverUrl: "https://collect.example.com/amplitude",
    enableRequestBodyCompression: true
))
```

`serverUrl` overrides `serverZone` and `useBatch` for event uploads. Remote configuration and diagnostics still use the Amplitude endpoints for `serverZone`.

The SDK gzip-compresses request bodies and sets `Content-Encoding: gzip` for Amplitude's endpoints. For a custom `serverUrl`, it sends uncompressed bodies unless you set `enableRequestBodyCompression: true`, because your proxy must accept gzip bodies. Compression requires 1.17.1 or later, and `enableRequestBodyCompression` requires 1.17.3 or later.

Your proxy must:

- Forward the request body and `Content-Encoding` header unchanged, or decompress before forwarding.
- Forward the client's IP address, for example in `X-Forwarded-For`, if you want Amplitude to derive location from it. Otherwise, Amplitude records the proxy's IP.
- Return Amplitude's status code and JSON body unchanged. The SDK reads them to decide whether to retry, split, or drop events.
- Use HTTPS, or add an App Transport Security exception for its domain.

For setup examples, go to [Proxy requests](https://amplitude.com/docs/sdks/analytics/proxy-requests).

### Use offline mode

By default, the SDK watches network connectivity with `NWPathMonitor`. While the device has no connection, it sets `offline` to `true`, keeps tracking events to storage, and skips uploads. When the connection returns, it sets `offline` to `false` and uploads the stored events. Requires 1.3.0 or later. watchOS has no connectivity monitor, so the SDK relies on connection errors and retries there.

To decide connectivity yourself, turn off the monitor and set `offline` from your own logic:

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    offline: NetworkConnectivityCheckerPlugin.Disabled
))

// Stop uploads, for example while the user is on a metered connection.
amplitude.configuration.offline = true

// Resume uploads and send what's stored.
amplitude.configuration.offline = false
amplitude.flush()
```

`NetworkConnectivityCheckerPlugin.Disabled` is `nil`. With the monitor off, nothing resets `offline` after the SDK sets it to `true` because of failed retries or a connection error, so set it back yourself.

### Store events somewhere else

`storageProvider` replaces the file storage for events and identity, and `identifyStorageProvider` replaces the storage for held identify events. Pass any type that implements the `Storage` protocol. The built-in storage classes aren't public, so a custom provider implements every method itself. This option isn't available in Objective-C. For the protocol, go to [Storage](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/reference#storage).

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    storageProvider: MyEventStorage()
))
```

The default storage keeps event files in Application Support, which the Files app doesn't show, and excludes them from backups. [Default changed in 1.18.2](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk-release-notes#1182). On tvOS, the files are in Caches. [Default changed in 1.18.7](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk-release-notes#1187). The SDK moves files from the earlier location on first launch.

### Set the log level

The SDK writes its own log messages with `os_log`, under the subsystem `Amplitude` and category `Logging`. Set `logLevel` to choose how much it logs. The default is `.warn`.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    logLevel: .debug
))
```

| Level | Logs |
| --- | --- |
| `.off` | Nothing. |
| `.error` | Errors, such as failed uploads. |
| `.warn` | Errors and warnings, such as an invalid revenue object. |
| `.log` | Errors, warnings, and informational messages, such as `Start flushing <n> events`. |
| `.debug` | Everything, including each request result. |

The SDK applies `logLevel` when you create the instance. Changing it afterward has no effect. The lowercase names require 1.17.0 or later. The uppercase names, such as `LogLevelEnum.DEBUG`, still work but are deprecated.

To send SDK messages somewhere else, such as your error reporting, set `loggerProvider` to a type that implements `Logger`. The SDK sets the logger's `logLevel` from the `logLevel` option at init, so filter on it in each method.

```swift
final class ErrorReportingLogger: Logger {
    typealias LogLevel = LogLevelEnum
    var logLevel: Int = LogLevelEnum.warn.rawValue

    func error(message: String) {
        // Forward to your error reporting at every level, then print.
        if logLevel >= LogLevelEnum.error.rawValue { print("Amplitude error: \(message)") }
    }
    func warn(message: String) {
        if logLevel >= LogLevelEnum.warn.rawValue { print("Amplitude warn: \(message)") }
    }
    func log(message: String) {
        if logLevel >= LogLevelEnum.log.rawValue { print("Amplitude log: \(message)") }
    }
    func debug(message: String) {
        if logLevel >= LogLevelEnum.debug.rawValue { print("Amplitude debug: \(message)") }
    }
}

let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    logLevel: .error,
    loggerProvider: ErrorReportingLogger()
))
```

To trace a missing event through the SDK, go to [Verify events](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/verify-events).

## Privacy

The iOS Swift SDK collects device and app context with every event. This section covers how to stop tracking for a user, reduce what the SDK collects, and describe the SDK's data use to Apple.

### Opt users out of tracking

Set `optOut` to `true` to stop the SDK from tracking events for this user.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    optOut: true
))

// Later, after the user consents:
amplitude.optOut = false
```

While opt-out is on:

- The SDK drops every new event, including identify, group, revenue, and session events, and logs `Skip event based on opt out configuration` at the `.log` level.
- Events already stored before opt-out still upload.
- The SDK notifies plugins that implement `onOptOutChanged(_:)` when the value changes.

The SDK doesn't persist opt-out. Each new `Amplitude` instance starts with the configured value, so store the user's choice yourself and pass it to the configuration on every launch.

### Choose which device properties the SDK collects

By default, the SDK adds the app version name, OS name and version, device manufacturer and model, carrier, language, platform, and the IDFV to every event. It also sends `ip: "$remote"`, so Amplitude records the request IP and derives location from it. On iOS, the SDK reads the carrier from `CTTelephonyNetworkInfo` on devices with a SIM, and sends `Unknown` elsewhere.

To stop collecting a field, pass a `TrackingOptions` object with the matching `disableTrack` method. Each method returns the object, so calls chain.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    trackingOptions: TrackingOptions()
        .disableTrackCarrier()
        .disableTrackIpAddress()
        .disableTrackLanguage()
))
```

| Method | Field the SDK stops sending |
| --- | --- |
| `disableTrackVersionName()` | `version_name` |
| `disableTrackOsName()` | `os_name` |
| `disableTrackOsVersion()` | `os_version` |
| `disableTrackDeviceManufacturer()` | `device_manufacturer` |
| `disableTrackDeviceModel()` | `device_model` |
| `disableTrackCarrier()` | `carrier` |
| `disableTrackIpAddress()` | `ip`. Amplitude then can't derive location from the IP. |
| `disableTrackCountry()` | `country`. The SDK sends `country`, from the device locale, only when IP tracking is off. |
| `disableTrackIDFV()` | `idfv`. The SDK also stops using the IDFV as the device ID. |
| `disableTrackLanguage()` | `language` |
| `disableTrackPlatform()` | `platform` |
| `disableTrackCity()` | Nothing. The SDK never sends `city`. Has no effect as of 1.19.4. |
| `disableTrackDMA()` | Nothing. The SDK never sends `dma`. Has no effect as of 1.19.4. |
| `disableTrackRegion()` | Nothing. The SDK never sends `region`. Has no effect as of 1.19.4. |

To keep Amplitude from deriving city, region, and DMA from the IP, use `disableTrackIpAddress()`. Tracking options control only what the SDK sends from now on. They don't delete data Amplitude already has.

Pass a new `TrackingOptions()` object, as in the sample, rather than changing `configuration.trackingOptions` in place. Every configuration created without `trackingOptions` shares one default object, so a change to it affects every instance in the app.

### Comply with COPPA

For apps directed at children under 13, set `enableCoppaControl` to `true`. With each event, the SDK turns off IDFV, IP address, city, and IDFA tracking on the configuration's tracking options.

```swift
let amplitude = Amplitude(configuration: Configuration(
    apiKey: "YOUR_API_KEY",
    trackingOptions: TrackingOptions().disableTrackIDFV(),
    enableCoppaControl: true
))
```

Pass `TrackingOptions().disableTrackIDFV()` as in the sample, for two reasons:

- The SDK chooses the device ID before it applies COPPA settings to the first event. Without `disableTrackIDFV()`, the device ID on a new install is still the IDFV.
- COPPA control changes the tracking options object in place. Without your own object, it changes the shared default, which turns off IDFV and IP tracking for every other instance in the app.

The SDK never collects the IDFA or the city, so COPPA control removes the IDFV and the IP address in practice. With IP tracking off, the SDK sends the `country` from the device locale.

### Describe data use in your privacy manifest

The SDK ships a privacy manifest, `PrivacyInfo.xcprivacy`, in its Swift package and CocoaPods resource bundle. Xcode includes it in your app's privacy report. It declares the SDK's defaults:

| Key | Value in the SDK's manifest |
| --- | --- |
| `NSPrivacyTracking` | Not present. The SDK doesn't use data for tracking as Apple defines it. |
| `NSPrivacyTrackingDomains` | Not present. |
| `NSPrivacyCollectedDataTypes` | Product interaction, device ID, and coarse location. Each is linked to the user, not used for tracking, and used for analytics. Coarse location comes from the IP address, not GPS. |
| `NSPrivacyAccessedAPITypes` | User defaults (`NSPrivacyAccessedAPICategoryUserDefaults`), reason `1C8F.1`. The SDK stores identity and session state in `UserDefaults`. |

Update your app's own manifest to match how you use the SDK:

- If you call `setUserId()` or set `userId`, declare the User ID data type.
- If your app uses Amplitude data for tracking, as Apple defines it in [User privacy and data use](https://developer.apple.com/app-store/user-privacy-and-data-use/), set `NSPrivacyTracking` to `true`. Then list the endpoint domains you send to in `NSPrivacyTrackingDomains`: `api2.amplitude.com` for the US region, `api.eu.amplitude.com` for the EU region, or your proxy's domain.

For how to read the combined report, go to Apple's [privacy manifest documentation](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files).

## Plugins

Plugins change events before Amplitude receives them, send events to other destinations, or react to identity changes. The SDK's own features, such as device context and uploads, are plugins too.

### How plugins run

Each plugin has a `type`. For each event, the SDK runs every `.before` plugin, then every `.enrichment` plugin, then every `.destination` plugin, in the order you added them within each type.

| Type | Base class | Receives events | Use it to |
| --- | --- | --- | --- |
| `.before` | `BeforePlugin` | Yes, first | Change or drop events before the SDK adds context. |
| `.enrichment` | `EnrichmentPlugin` | Yes | Add or change properties. |
| `.destination` | `DestinationPlugin` | Yes, last | Send events somewhere else. The Amplitude upload is a destination. |
| `.observe` | `ObservePlugin` | No | React to user ID, device ID, session, and opt-out changes. |
| `.utility` | `UtilityPlugin` | No | Run setup code with access to the instance. |

A before or enrichment plugin that returns `nil` from `execute(event:)` drops the event for every later plugin, including the Amplitude upload. `execute(event:)` runs for every event, including identify, group identify, and revenue events. Every destination gets the event that the enrichment plugins returned, and its return value doesn't affect other destinations.

### Add a plugin

Subclass a base class, override `execute(event:)`, and pass an instance to `add(plugin:)`. The SDK calls the plugin's `setup(amplitude:)` right away.

```swift
class AppSectionPlugin: EnrichmentPlugin {
    override func execute(event: BaseEvent) -> BaseEvent? {
        var properties = event.eventProperties ?? [:]
        properties["app_section"] = "checkout"
        event.eventProperties = properties
        return event
    }
}

let appSection = AppSectionPlugin()
amplitude.add(plugin: appSection)
```

To stop a plugin, pass the same instance to `remove(plugin:)`:

```swift
amplitude.remove(plugin: appSection)
```

To drop events, return `nil`:

```swift
class DropDebugEventsPlugin: BeforePlugin {
    override func execute(event: BaseEvent) -> BaseEvent? {
        return event.eventType.hasPrefix("Debug ") ? nil : event
    }
}
```

The base classes don't declare `name` or `onReset()`, so `plugin(name:)` doesn't find a subclass that adds them, and the SDK doesn't notify it on `reset()`. When you need either, conform a class to the `Plugin` protocol directly. The SDK skips a plugin whose `name` matches one already added.

```swift
final class ResetAwarePlugin: Plugin {
    let type: PluginType = .enrichment
    let name: String? = "reset-aware"

    func execute(event: BaseEvent) -> BaseEvent? { event }
    func onReset() {
        // Clear state tied to the signed-out user.
    }
}
```

### Send events to another destination

Subclass `DestinationPlugin` and override the methods for the event kinds you need. The SDK calls `track(event:)` for custom events, group identify events, and autocaptured events. It calls `identify(event:)` for `$identify` and `revenue(event:)` for `revenue_amount`. `flush()` runs when you call `amplitude.flush()`.

```swift
class WarehouseDestinationPlugin: DestinationPlugin {
    override func track(event: BaseEvent) -> BaseEvent? {
        // Send the event to your own endpoint.
        return event
    }

    override func identify(event: IdentifyEvent) -> IdentifyEvent? {
        return event
    }

    override func revenue(event: RevenueEvent) -> RevenueEvent? {
        return event
    }

    override func flush() {
        // Send anything your plugin buffers.
    }
}

amplitude.add(plugin: WarehouseDestinationPlugin())
```

Your destination receives every event as it's tracked. It doesn't receive the SDK's held identify batching or its retries.

### React to identity changes

Subclass `ObservePlugin` and override the hooks you need: `onUserIdChanged(_:)`, `onDeviceIdChanged(_:)`, `onSessionIdChanged(_:)`, and `onOptOutChanged(_:)`.

```swift
class SignInObserverPlugin: ObservePlugin {
    override func onUserIdChanged(_ userId: String?) {
        // For example, update another SDK with the new user ID.
    }
}

amplitude.add(plugin: SignInObserverPlugin())
```

For every hook and the `Plugin` protocol, go to [Plugin](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/reference#plugin). For a plugin that prints each event while you debug, go to [Verify events](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/verify-events).

## Objective-C

The SDK exposes an Objective-C interface through wrapper classes. The instance class is `Amplitude`, and the other classes use the `AMP` prefix, such as `AMPConfiguration`, `AMPBaseEvent`, `AMPIdentify`, and `AMPRevenue`. Import the module, and also import `AmplitudeCore` to use `AMPServerZone` values:

```objc
@import AmplitudeSwift;
@import AmplitudeCore;

AMPConfiguration *configuration = [AMPConfiguration initWithApiKey:@"YOUR_API_KEY"];
configuration.autocapture = [[AMPAutocaptureOptions alloc] initWithOptionsToUnion:@[
    AMPAutocaptureOptions.sessions,
    AMPAutocaptureOptions.appLifecycles,
]];
configuration.serverZone = AMPServerZoneEU;
configuration.logLevel = AMPLogLevelDEBUG;
Amplitude *amplitude = [Amplitude initWithConfiguration:configuration];

AMPBaseEvent *event = [AMPBaseEvent initWithEventType:@"Song Played"
                                      eventProperties:@{@"title": @"Here Comes the Sun"}];
[amplitude track:event];

AMPIdentify *identify = [AMPIdentify new];
[identify set:@"plan" value:@"pro"];
[amplitude identify:identify];

[amplitude setUserId:@"user-12345"];
[amplitude reset];
```

Write plugins with blocks:

```objc
AMPPlugin *plugin = [AMPPlugin initWithType:AMPPluginTypeEnrichment
                                    execute:^AMPBaseEvent * _Nullable(AMPBaseEvent * _Nonnull event) {
    [event.eventProperties set:@"app_section" value:@"checkout"];
    return event;
}];
[amplitude add:plugin];
```

The Objective-C interface has these limits:

- No `storageProvider`, `identifyStorageProvider`, `maxQueuedEventCount`, or `enableDiagnostics` configuration options.
- `optOut` is a property of `AMPConfiguration`, not of `Amplitude`. Set it on `amplitude.configuration`.
- Network tracking rules take only `hosts` and `statusCodeRange`. URL patterns, methods, headers, and bodies need Swift.
- `AMPPlugin` supports `setup`, `execute`, and `flush` blocks only, not the per-kind destination methods or the observe hooks. You can also pass a Swift plugin to `add:`.
- The `loggerProvider` block receives the configured log level, not the level of each message.

For the selector of each method, go to [Objective-C names](https://amplitude.com/docs/sdks/analytics/ios/ios-swift-sdk/reference#objective-c-names).

