Verify iOS Swift SDK events
このページはまだあなたの言語に翻訳されていません。 現在取り組んでいますので、後でもう一度確認してください。
An event moves through four stages between your track() call and Amplitude's servers. Verify each stage in order. The first stage where the event looks wrong is where the problem is.
| Stage | Question | Where to look |
|---|---|---|
| Captured | Did the SDK accept the call? | Debug logs in the Xcode console, and the SDK's identity state |
| Transformed | What did plugins add, change, or drop? | An inspection plugin |
| Sent | Did the request leave the device, and with what payload? | Debug logs, a local proxy, or a capture endpoint |
| Received | Did Amplitude accept and store the event? | The event callback, then User Lookup in Amplitude |
For problems common to every Amplitude SDK, go to SDK troubleshooting and debugging. If behavior changed after an SDK upgrade, check the iOS Swift SDK release notes.
Turn on debug logging
Pass logLevel: .debug to Configuration while you verify. The SDK reads logLevel only when you create the instance, so setting amplitude.configuration.logLevel later has no effect.
let amplitude = Amplitude(configuration: Configuration(
apiKey: "YOUR_API_KEY",
logLevel: .debug
))
The levels are .off, .error, .warn (default), .log, and .debug. The uppercase names, such as LogLevelEnum.DEBUG, still compile but are deprecated. The lowercase names require 1.17.0 or later.
The default logger writes to the unified logging system with subsystem Amplitude and category Logging. Each message starts with its level: Error: , Warn: , Log: , or Debug: . In the Xcode console, filter on subsystem:Amplitude. Run the app from Xcode to read the messages. Outside a debugger session, tools such as Console.app can show the message text as <private>.
Return logLevel to .warn before you ship. To send SDK logs to your own logging system instead, go to Set the log level.
Check what the SDK captured
The SDK doesn't log individual track() calls. With debug logging on, a successful capture and upload includes lines like these:
Debug: Installing NetworkConnectivityCheckerPlugin, offline feature should be supported.
Debug: Trimming queued events..
Log: Start flushing 1 events
Debug: Successfully completed request
Debug: No event files to upload
Start flushing N events counts the events queued since the last upload, so it confirms that the SDK accepted your call. If the line never appears after flush(), check these causes:
- The call didn't reach the configured instance. The call never ran, or it ran on a different
Amplitudeinstance from the one you configured. Each instance has its own queue and settings. - The instance was released. An instance stored in a local variable stops uploading when Swift frees it. Keep a strong reference for the app's lifetime.
- Opt-out is on. The SDK logs
Log: Skip event based on opt out configurationand drops the event before it reaches the queue. - The SDK is offline.
flush()logsDebug: Skipping flush while offline.and keeps the events. Go to Use offline mode.
At this stage, you can also read the state the SDK attaches to every event:
amplitude.getDeviceId() // "EAB4A5BE-2294-4202-B9A8-EC96F02BA9D5"
amplitude.getUserId() // "user-12345", or nil
amplitude.getSessionId() // 1793655975244
amplitude.optOut // false
On iOS, tvOS, and visionOS, the SDK uses the identifier for vendor (IDFV) as the default device ID, so device_id and idfv match in the payload. On macOS, it uses the Mac's network hardware address.
Check how plugins transformed the event
After capture, each event passes through the plugin pipeline in a fixed order:
- Before plugins (
.before): the built-in context plugin addsdevice_id,user_id,session_id,time,event_id,insert_id,library,platform,os_name,os_version,device_model,language,ip, and the other device fields. - Enrichment plugins (
.enrichment): your own plugins run here, in the order you add them. - Destination plugins (
.destination): the built-in Amplitude destination queues the event for upload.
A before or enrichment plugin that returns nil drops the event. The SDK doesn't log the drop, and the callback doesn't fire for it.
To see each event after your other plugins have changed it, add an enrichment plugin that prints the event, and add it after your other plugins:
class InspectEventsPlugin: EnrichmentPlugin {
override func execute(event: BaseEvent) -> BaseEvent? {
if let data = try? JSONEncoder().encode(event),
let json = String(data: data, encoding: .utf8) {
print("[InspectEventsPlugin] \(json)")
}
return event
}
}
let inspector = InspectEventsPlugin()
amplitude.add(plugin: inspector)
The printed JSON uses the same keys as the upload, such as event_type, event_properties, device_id, and insert_id. Compare it with what you expect. Remove the plugin with amplitude.remove(plugin: inspector) when you're done.
Check what the SDK sent
The SDK uploads events with a POST request to the endpoint for your configuration:
| Configuration | Request URL |
|---|---|
| Default (US) | https://api2.amplitude.com/2/httpapi |
serverZone: .EU | https://api.eu.amplitude.com/2/httpapi |
useBatch: true (US) | https://api2.amplitude.com/batch |
useBatch: true (EU) | https://api.eu.amplitude.com/batch |
serverUrl set | The URL you set |
Requests go out every flushIntervalMillis (30 seconds by default), when the queue reaches flushQueueSize (30 events by default), when the app moves to the background, and when you call flush(). The SDK sends one request at a time.
The debug log shows that a request finished, but not its payload: Debug: Successfully completed request on success, or Error: Response failed with code<status>: <response body> on failure. To read the payload, use one of these methods:
Local proxy: Use a proxy such as Proxyman or Charles. Install the proxy's root certificate on the simulator or device and turn on SSL proxying for
api2.amplitude.com(orapi.eu.amplitude.com).Capture endpoint: Point
serverUrlat a local server that logs request bodies. The simulator shares your Mac's network, solocalhostreaches it. On a device, use your Mac's address, and add an App Transport Security exception if the URL useshttp://.swiftlet amplitude = Amplitude(configuration: Configuration( apiKey: "YOUR_API_KEY", serverUrl: "http://localhost:8080/2/httpapi" ))To accept the upload, the server must answer
200with a body such as{"code":200,"events_ingested":1,"payload_size_bytes":1,"server_upload_time":1793655976780}.
The request body has this shape. Every event carries every field, with null for the ones the SDK didn't set. This excerpt omits most of the null fields:
{
"api_key": "YOUR_API_KEY",
"client_upload_time": "2026-11-02T21:46:16.996Z",
"events": [
{
"event_type": "Sign Up Clicked",
"event_properties": { "plan": "starter" },
"device_id": "EAB4A5BE-2294-4202-B9A8-EC96F02BA9D5",
"idfv": "EAB4A5BE-2294-4202-B9A8-EC96F02BA9D5",
"user_id": null,
"session_id": 1793655976959,
"time": 1793655976959,
"event_id": 1,
"insert_id": "B3E99093-7A57-4F37-914F-072F69224BDB",
"library": "amplitude-swift/1.19.4",
"platform": "iOS",
"os_name": "ios",
"os_version": "27.0",
"device_manufacturer": "Apple",
"device_model": "arm64",
"carrier": "Unknown",
"language": "en-US",
"ip": "$remote",
"version_name": "16.0",
"app_version": null
}
]
}
"ip": "$remote" tells Amplitude to use the request's IP address. The SDK doesn't set app_version for you. version_name holds the app's CFBundleShortVersionString. When you set minIdLength, the body also includes "options": {"min_id_length": N}. The body can also include a request_metadata object with SDK diagnostics. The enableDiagnostics option controls this object.
Compressed request bodies
For Amplitude's own endpoints, the SDK gzip-compresses every request body and sends Content-Encoding: gzip. A proxy shows binary data unless it decodes gzip. When you set serverUrl, the SDK sends uncompressed JSON unless you also set enableRequestBodyCompression: true.
If no request appears:
| Cause | How to confirm | Fix |
|---|---|---|
| The next scheduled upload hasn't run yet | No Start flushing line since the track() call. | Call amplitude.flush(). |
| The SDK is offline | Debug: Skipping flush while offline. amplitude.configuration.offline is true. | The SDK uploads when the network returns. If your app sets offline, set it to false and call flush(). |
| The host is unreachable or blocked | Error: Conection failed with error: ..., marking offline | Check the device's network, the serverUrl value, and App Transport Security for http:// URLs. |
| Uploads failed too many times | Error: Request failed more than 6 times, marking offline | Fix the error in the earlier Response failed lines, then set amplitude.configuration.offline = false and call flush(). |
Check what Amplitude received
Read the upload result
Pass a callback to Configuration. The SDK calls the callback once for each event in a request, with the final event, a code, and a message:
let amplitude = Amplitude(configuration: Configuration(
apiKey: "YOUR_API_KEY",
callback: { event, code, message in
print(event.eventType, code, message)
}
))
To get the result for one event only, pass callback to track(event:options:callback:). Both callbacks fire. For the callback's signature, go to Callback.
code | message | Meaning | What the SDK does |
|---|---|---|---|
200 | Successfully send event | Amplitude accepted the event. | Removes the event from storage. |
400 | Invalid API key: <your key> | Amplitude doesn't recognize the key in this data center. | Drops every event in the request. |
400 | Server error text | Amplitude rejected this event, for example for invalid or missing fields. | Drops the events the server named, and queues the rest of the request again. |
413 | Server error text, for example Payload too large | The event is too large to upload on its own. | Drops the event. For a larger batch, the SDK splits it in half and retries first. |
| No call | n/a | 429, 408, 5xx, a network error, opt-out, or a plugin dropped the event. | Retries uploads with backoff. Drops opt-out and plugin-dropped events before upload. |
A 200 response means Amplitude's ingestion API accepted the event. Amplitude can still exclude an accepted event from analysis later, for example when a block filter matches it.
The SDK keeps per-event callbacks in memory only. If the app quits before the upload, the event still uploads on the next launch, but its per-event callback doesn't fire.
Find the event in Amplitude
- Open User Lookup and search for the event's
user_id, or itsdevice_idif the user is anonymous. - Select the Activity tab and turn on Live event updates.
- Open the event and compare its properties with the inspection plugin's output.
For events from many users, use the Ingestion Debugger to review accepted and rejected requests for the project. Amplitude's chart queries can take a few minutes longer to include new events than User Lookup does.
これは役に立ちましたか?
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.