VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Groups
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
Swift SDK
Swift quickstart
Display a paywall
Make and restore purchases
Check access
Identify users
Evaluate feature flags
Capture analytics
Groups
Track screens
Development mode
Swift SDK reference
Errors
Diagnostic codes
Troubleshooting

Capture analytics

Capture events, report opened notifications, and manage analytics consent on iOS.

The SDK records events for the current person and delivers them to Voidhash in background batches. You don't need a separate analytics package. The event model and built-in $ events are explained in Events.

Capture an event

await voidhash.capture("upgrade_button_clicked", properties: [
    "source": .string("settings"),
    "plan": .string("monthly"),
])

Use clear, stable names. Names that start with $ are reserved for Voidhash's own events. A number that isn't finite is sent as null.

Automatic events

EventWhen
$app_installedThe first launch of this install.
$app_updatedThe first launch after the app's version or build changed.
$app_openedThe first time the app comes to the foreground in a process. A background launch, such as a silent push, records none until the user opens the app.
$app_backgroundedThe app moved to the background.
$app_became_activeThe app returned to the foreground in the same process.
$app_crashedThe app crashed. See Crashes.
$sign_outsignOut() or reset() was called.
$screenA screen appeared. See Track screens.

To record the app events and $sign_out yourself, set options.automaticLifecycleEvents = false. The SDK still saves its queue when the app leaves and resumes delivery when it returns.

Crashes

$app_crashed comes from the crash reports iOS hands the SDK on the next launch. The SDK installs no signal or exception handler, so it doesn't interfere with your crash reporter. The event carries crash_kind (crash) and $app_version, the version that crashed, but no stack trace. iOS decides which reports it hands over, so counts can be incomplete. Reports aren't read on macOS or visionOS, and the Simulator doesn't produce them.

Report opened notifications

When a user opens a notification sent through Voidhash, pass it to the SDK from your notification delegate. It records $push_opened, which push open rates are built from:

func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse
) async {
    Voidhash.shared?.trackPushOpened(userInfo: response.notification.request.content.userInfo)
}

The SDK doesn't replace your notification delegate, so this call is how it learns about opens. Pass every notification you handle: the SDK ignores those without the voidhash_send_id key Voidhash adds to the ones it sends. Reporting the same notification twice in a row counts once. Registering device tokens isn't part of the SDK; see Register devices for push.

Sessions

Every event carries a session ID. A session ends after 30 minutes without an event, including time in the background, and survives app restarts within that time. Signing out starts a new session.

let sessionId = await voidhash.sessionId()

Delivery

While the app is in the foreground, the SDK sends a batch when 20 events are queued or every 5 seconds. Failed requests are retried with growing delays, at most 30 seconds apart when Voidhash asks the SDK to slow down. When the app moves to the background, the SDK sends what is queued once more.

The device keeps up to 1,000 undelivered events across restarts. Beyond that the oldest are dropped and the SDK reports ANALYTICS_EVENT_DROPPED, at most once a minute. When Voidhash refuses one event as invalid, only that event is dropped. Each event keeps the app version, OS version, locale and time zone from when it was captured.

Before an important boundary, such as a screen the user may not return from, send the queue now:

await voidhash.flush()

flush() sends identify calls and attributes first, then reported purchases, then up to 10 batches of events. It waits up to 2 seconds and returns a FlushStatus with flushed and pending counts; delivery continues in the background.

Analytics consent

When a user declines usage analytics or hasn't decided yet, tell the SDK:

await voidhash.setAnalyticsConsent(.denied) // .granted, .denied or .pending
let consent = voidhash.analyticsConsent

.denied and .pending behave the same:

  • No custom events, screens, lifecycle events, $sign_out or paywall events are queued, stored or sent, and no session is kept. $app_installed and $app_updated are recorded once consent is granted; crashes and opened notifications from this time are never reported.
  • Person attributes aren't sent, and setPersonAttributesState returns .skipped.
  • Apple Ads attribution doesn't run.
  • The first skipped call reports the ANALYTICS_CONSENT_DENIED diagnostic.
  • Purchases, restores, access checks, feature flags and paywalls work as usual. identify still links the user and sends the email and name you pass.
  • Requests carry an x-analytics-consent header, so Voidhash records no paywall exposure for them.

Withdrawing consent discards the events still on the device. Granting it collects from then on; nothing skipped is sent later.

options.analyticsConsent sets the consent at launch and defaults to .granted. Set it to .pending when your app asks first. A choice you set at runtime is stored and takes precedence on later launches, and survives sign-out. Revenue reporting is unaffected; metrics built from events, such as paywall conversion, count only users who granted consent.

Share purchases with ad platforms

When you send purchases to Meta, Voidhash sends a person's purchases only after they agreed to it. That agreement is separate from analytics consent. Record it with:

try await voidhash.setAdDataSharing(true)

This sets the person attribute $ad_data_sharing and, like any attribute, needs analytics consent. Taking it back with setAdDataSharing(false) works with or without consent: the SDK stores it and keeps sending it, for up to 48 hours if Voidhash has no person yet. Agreeing again drops a revocation that is still queued; if that revocation is already on its way to Voidhash, setAdDataSharing(true) returns deferred and the agreement is sent right after it. Declining analytics doesn't revoke it, so when someone rejects everything, call both setAdDataSharing(false) and setAnalyticsConsent(.denied).

Credit installs to Apple Ads

On a new install, the SDK asks Apple's AdServices framework whether an Apple Ads campaign brought the user, and records the campaign, ad group and ad on the person as their first and latest ad touch. It sends no advertising identifier and needs no App Tracking Transparency prompt. It runs once per install in the background, only with analytics consent.

When Apple can't answer yet or the device is offline, a later launch asks again, up to 10 launches. The SDK reports INSTALL_ATTRIBUTION_DEFERRED when it will ask again and INSTALL_ATTRIBUTION_ABANDONED when it gives up. Installs that ran an earlier SDK version, and app extensions, aren't looked up. Turn it off with options.installAttribution = false.

iOS can't credit two kinds of install to a person: links from your website, because the App Store has no referrer (use web-to-app handoff), and Meta or TikTok app-install ads, which have no path to an individual install without tracking. See Ad attribution in your app.

Next steps

  • Track screens
  • Events
  • Check your data

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousEvaluate feature flagsNextGroups

On this page

Capture an eventAutomatic eventsCrashesReport opened notificationsSessionsDeliveryAnalytics consentShare purchases with ad platformsCredit installs to Apple AdsNext steps