Instrument and configure the iOS Swift SDK
이 페이지는 아직 귀하의 언어로 번역되지 않았습니다. 현재 작업 중이므로 곧 다시 확인해 주십시오.
After you 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. To find the release that added or changed a feature, go to iOS Swift SDK release notes.
The samples use Swift. For the Objective-C names of each class and method, go to 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.
Track a custom event
Pass an event name and, optionally, a dictionary of event properties.
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.
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.
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().
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.
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 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:).
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:
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.
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:
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.
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.
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.
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.
logRevenue() is deprecated and has no effect as of 1.19.4. It doesn't send an event. Use revenue(revenue:).
To get the most from revenue data, also track products with the product array 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.
// 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.
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
- The
deviceIdconfiguration option. Requires 1.19.0 or later. - The device ID stored from an earlier launch.
- The identifier for vendor (IDFV), unless you turned it off with
trackingOptions.disableTrackIDFV(). - 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, after the user grants App Tracking Transparency permission. For an example, go to IDFACollectionPlugin.swift in the SDK repository.
User ID
- The
userIdconfiguration option. Requires 1.19.0 or later. - 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.
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.
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.
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.
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:
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.
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
minTimeBetweenSessionsMillisof 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_startat the start of each session andsession_endat the end of the previous one. Amplitude shows them as[Amplitude] Start Sessionand[Amplitude] End Session. The SDK sendssession_endwhen the next session starts, not when the user leaves, so a user who never returns has nosession_end. - With session autocapture off, events still carry a session ID. Only the
session_startandsession_endevents 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:
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.
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.
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.
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.
Choose features
autocapture takes an AutocaptureOptions option set. The default is .sessions.
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 | .sessions | On | All | session_start, session_end |
| App lifecycles | .appLifecycles | Off | UIKit platforms | [Amplitude] Application Installed, [Amplitude] Application Updated, [Amplitude] Application Opened, [Amplitude] Application Backgrounded |
| Screen views | .screenViews | Off | UIKit platforms | [Amplitude] Screen Viewed |
| Element interactions | .elementInteractions | Off | UIKit platforms | [Amplitude] Element Interacted |
| Frustration interactions | .frustrationInteractions | Off | UIKit platforms | [Amplitude] Rage Click, [Amplitude] Dead Click |
| 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. 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.
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:
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. |
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 Clickwith the element properties above, plus[Amplitude] Begin Time,[Amplitude] End Time(ISO 8601),[Amplitude] Duration(milliseconds),[Amplitude] Click Count, and[Amplitude] Clicks, an array ofX,Y, andTimefor 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 Clickwith the element properties, plus[Amplitude] Xand[Amplitude] Y. Taps inside aWKWebViewdon't count.
Dead clicks need a plugin that reports interface changes to the SDK, such as the Session Replay iOS plugin, installed and recording. Without one, the SDK never sends [Amplitude] Dead Click.
To turn off one kind, set interactionsOptions:
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.
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:
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.comreturns503: captured, by the second rule.api.example.comreturns401: not captured. The second rule matches the host, and401isn't in its range.cdn.example.netreturns401: captured, by the first rule.ads.notmyapi.comreturns500: 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.
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
captureSafeHeaderson (the default), the SDK also captures a fixed list of non-sensitive headers, such ascontent-typeandcache-control. It never capturesauthorization,cookie, orproxy-authorization, even when you allow them. - Body paths are case-sensitive.
*matches one level and**matches any number of levels.excludelistremoves paths thatallowlistselected. - 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
- 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.
- An upload starts when the first of these happens:
flushIntervalMillis(default30000) passes. The timer repeats.- The number of events since the last upload reaches
flushQueueSize(default30). - The app moves to the background, when
flushEventsOnCloseistrue(the default). - You call
flush(). - The device reconnects to a network.
- 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
flushQueueSizeevents, for example after a period offline. - 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
400with the messageInvalid API key: <key>. - 400 for specific events: drops the events Amplitude lists as invalid or silenced, reports
400for 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.
- After each failed retry, the SDK waits 1, 2, 4, and then up to 60 seconds before the next attempt. After more than
flushMaxRetries(default6) failures in a row, it setsofflinetotrueand stops uploading. Default changed in 1.11.9. Uploads resume when the network status changes, when you setofflinetofalse, or on the next launch. The events stay on the device until then. - A connection error, such as no internet connection, an unreachable host, or an App Transport Security block, sets
offlinetotrueright away.
The SDK asks iOS for background execution time while an upload runs, so a request can finish after the user leaves the app.
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
flushIntervalMillisorflushQueueSize. Events wait longer on the device before they upload. - Faster delivery: lower
flushIntervalMillisorflushQueueSize. The SDK sends more, smaller requests and uses more battery. - High-volume sources: set
useBatch: trueto send to the Batch Event Upload API, 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.
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.
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-Encodingheader 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.
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:
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.
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. On tvOS, the files are in Caches. Default changed in 1.18.7. 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.
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.
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.
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.
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 configurationat the.loglevel. - 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.
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.
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 setuserId, declare the User ID data type. - If your app uses Amplitude data for tracking, as Apple defines it in User privacy and data use, set
NSPrivacyTrackingtotrue. Then list the endpoint domains you send to inNSPrivacyTrackingDomains:api2.amplitude.comfor the US region,api.eu.amplitude.comfor the EU region, or your proxy's domain.
For how to read the combined report, go to Apple's privacy manifest documentation.
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.
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:):
amplitude.remove(plugin: appSection)
To drop events, return nil:
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.
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().
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(_:).
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. For a plugin that prints each event while you debug, go to 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:
@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:
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, orenableDiagnosticsconfiguration options. optOutis a property ofAMPConfiguration, not ofAmplitude. Set it onamplitude.configuration.- Network tracking rules take only
hostsandstatusCodeRange. URL patterns, methods, headers, and bodies need Swift. AMPPluginsupportssetup,execute, andflushblocks only, not the per-kind destination methods or the observe hooks. You can also pass a Swift plugin toadd:.- The
loggerProviderblock receives the configured log level, not the level of each message.
For the selector of each method, go to Objective-C names.
이 내용이 도움이 되었나요?
For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.