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
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
Track screens
Development mode
Swift SDK reference
Errors
Diagnostic codes
Troubleshooting

Swift SDK reference

Client options, diagnostics, observer mode and methods of the Voidhash iOS SDK.

Everything VoidhashOptions accepts and the client exposes. To install the SDK and create the client, see Swift SDK.

Client options

Pass a VoidhashOptions value to configure.

import Voidhash
import VoidhashUI

var options = VoidhashOptions()
options.paywallRenderer = VoidhashUIPaywallRenderer()
options.analyticsConsent = .pending

Voidhash.configure(publishableKey: "vh_pk_...", options: options)
OptionDefaultDescription
baseUrlhttps://api.voidhash.comAPI origin, for self-hosted deployments.
ingestUrlbaseUrlAnalytics origin only.
debugfalseLogs diagnostics and marks requests as coming from a debug build.
distinctIdGenerated anonymous IDStarts with a known user ID. See Start with a known ID.
enabledtruefalse makes the SDK inert. See Disabled clients.
readOnlyfalseObserver mode. See Observer mode.
devfalseDevelopment mode. Honored only in debug builds.
paywallRenderernilVoidhashUIPaywallRenderer(), required to present paywalls.
flowCapabilities[.links]What paywalls may do beyond purchases. See Choose what paywalls may do.
preloadPlacements[]Location slugs to download on the very first launch. See Preload locations.
featureFlagsSee pagedefaults, maxWaitMilliseconds (2000) and onUpdate. See Feature flags.
screenTrackingAutomaticautomatic, mapScreen and isAppScreenClass. See Track screens.
automaticLifecycleEventstrueRecords $app_* and $sign_out events. See Automatic events.
analyticsConsent.grantedConsent at launch. See Analytics consent.
installAttributiontrueCredits new installs to Apple Ads. See Credit installs to Apple Ads.
onWarningUnified logReceives messages about failures that aren't thrown, such as a failed background refresh.
onDiagnosticnilReceives the same situations as structured values. See Diagnostics.

The SDK keeps its on-device state per publishable key and API origin. A different key or baseUrl starts a new anonymous identity with empty queues, and nothing queued under the old one is sent. Keep both stable across app updates.

Diagnostics

onDiagnostic receives a VoidhashDiagnostic each time the SDK handles a failure with a fallback instead of throwing. Each value has a kind, a stable code (such as TRANSACTION_REJECTED), the operation, whether it's retryable, an optional httpStatus and a message. Use it to forward SDK health to your logging. The handler is called from any thread and must return quickly.

options.onDiagnostic = { diagnostic in
    Logger.voidhash.notice("\(diagnostic.kind.rawValue) \(diagnostic.code): \(diagnostic.message)")
}
kindMeaning
transportA request failed or timed out and will be retried.
evictionA queue reached its cap and dropped its oldest entry.
breakerThe SDK paused requests after repeated failures, or resumed them.
authVoidhash rejected the publishable key and requests are paused, or the key works again.
cacheA cache entry couldn't be read or written.
consentAnalytics or attributes were skipped because consent isn't granted.

Troubleshooting explains the codes you're most likely to see.

Observer mode

In observer mode, another billing SDK owns purchases. Set options.readOnly = true: Voidhash then reports StoreKit transactions without finishing them, and purchase(product:) throws READ_ONLY_PURCHASE_NOT_ALLOWED. Switch at runtime with setReadOnly(_:) and read the mode from isReadOnly; a purchase in progress keeps the mode it started with. Report purchases from another billing SDK covers reportTransaction and syncPurchases.

Disabled clients

With enabled = false the SDK makes no requests, opens no StoreKit connection, starts no observers and writes nothing to storage. getProducts() returns [], getCurrentPerson() returns nil, presentFlow returns .failed(.disabled), and getDistinctId() returns an anonymous ID that is never stored or sent.

Reconfigure and shut down

Calling configure again replaces the shared client. The previous client stops and hands its queued events and transactions to the new one. A reference to the old client stays safe to call: writes such as capture, identify and reportTransaction go to the new client when it uses the same key and API origin, and reads return the values a disabled client returns.

shutdown() retires a client the same way without replacing it. It ends the StoreKit connection, stops background work and writes the queues to disk.

Launches before the first unlock

iOS can launch an app in the background after a reboot, before the device is first unlocked. The SDK can't read its stored state then, so it waits for the unlock instead of starting a new identity. Until then, reads return empty, stale values without waiting, and events, identify calls and reported transactions are kept in memory.

App extensions

An extension is a separate process. The SDK doesn't use App Groups, so an extension has its own identity, queues and caches. To read the signed-in user's access in one, configure it with options.distinctId set to the ID your app passes to identify, and set automaticLifecycleEvents = false. Paywalls and store sheets aren't available in extensions.

Methods

MethodDescriptionGuide
waitForInitialization()Waits for initialization; returns the project schema.Create the client
getProducts()Products that resolved in the App Store, sorted by slug.Purchases
purchase(product:offerId:), purchase(productSlug:offerId:)Buys a product; returns .completed or .deferred.Purchases
restorePurchases()Restores the person's App Store purchases.Purchases
presentCodeRedemptionSheet(), showManageSubscriptions()App Store sheets.Purchases
reportTransaction(…), syncPurchases()Report purchases in observer mode.Purchases
pendingTransactionCount(), syncPendingWork()Receipts waiting for Voidhash; send everything queued now.Purchases
getCurrentPerson(forceFetch:), getCurrentPersonState(forceFetch:)The person and their perks; the state variant adds freshness.Check access
resetPersonCache()Drops the cached person so the next read asks Voidhash.Check access
identify(externalUserId:email:name:), identifyState(…)Links the device to your user ID.Identify users
setPersonAttributes(_:), setPersonAttributesState(_:)Sets person attributes.Identify users
signOut(), reset()Starts a new anonymous identity.Identify users
getDistinctId(), Voidhash.isAnonymousDistinctId(_:)The current distinct ID, and whether it's anonymous.Identify users
presentFlow(placement:), presentFlow(placement:from:), dismissFlow()Presents and closes paywalls.Paywalls
getPaywall(location:), getPaywallState(location:)Resolves a location's paywall without presenting it, for custom presenters.Paywalls
getFeatureFlags(_:), getFeatureFlagsState(_:)Evaluates feature flags.Feature flags
capture(_:properties:), screen(_:properties:), flush()Records events and sends the queue.Analytics
trackPushOpened(userInfo:)Records an opened push notification.Analytics
sessionId()The current analytics session ID.Analytics
setAnalyticsConsent(_:), analyticsConsentSets and reads analytics consent.Analytics
setAdDataSharing(_:)Records consent to share purchases with ad platforms.Analytics
setReadOnly(_:), isReadOnlySwitches observer mode at runtime.Observer mode
start(), shutdown()Starts or retires a client you created yourself.Reconfigure and shut down

Methods for embedding hosts

These exist for hosts that embed the client as their transport, such as the React Native SDK's native layer. Don't call them from a Swift app.

MethodWhat it does
VoidhashClient.makeEmbedded(publishableKey:options:)Creates a client that stores nothing and starts no observers; the host owns identity and caching.
fetchSchema(distinctId:), fetchPerson(distinctId:), evaluateFeatureFlags(distinctId:keys:), resolvePaywallConfig(distinctId:locationSlug:)Read the schema, person, flags or a location's paywall for an explicit distinct ID, without caching.
identifyPerson(distinctId:externalUserId:email:name:), setPersonTraits(distinctId:traits:)Identify a distinct ID or write person attributes, and return the updated person. setPersonAttributes is the app-facing form.
syncStoreTransaction(…)Records a store transaction the host observed in the durable outbox.
injectInternalSchema(_:), recordDevelopmentPurchase(…)Preview, testing and development-mode plumbing.
Methods starting with host, queuePersonAttributes(_:), shutdownAfterFlush()Behind @_spi(VoidhashHost): queue person attributes for the current identity without waiting, and flush (within the SDK's time budget) before retiring the client.

Next steps

  • Swift SDK
  • Display a paywall
  • Errors

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

PreviousDevelopment modeNextErrors

On this page

Client optionsDiagnosticsObserver modeDisabled clientsReconfigure and shut downLaunches before the first unlockApp extensionsMethodsMethods for embedding hostsNext steps