Skip to main content
The iOS SDK mirrors the browser SDK: configure it on startup, call getSession() when the user performs a sensitive action, and POST the sealed token to your backend for verification.
New iOS integrations are temporarily paused while the SDK moves to Foil’s unified session transport. The iOS SDK remains supported, but the currently published release will receive transport.upgrade_required until that update ships.

Requirements

  • iOS 14+ for the public binary SwiftPM package
  • Swift 5.9+
  • Xcode 15+
  • A Foil publishable key, starting with pk_live_ or pk_test_

Install

Add Foil with Swift Package Manager:
Then add the Foil product to your app target. In Xcode, choose File > Add Package Dependencies…, enter https://github.com/abxy-labs/foil-ios, choose version 1.1.0 or newer, and add the Foil product to the target that configures the SDK.

Configure on startup

Configure once at app launch - typically from your App initializer - so signal collection is running by the time the first screen appears.

FoilConfiguration

Runtime integrity and anti-tamper collection is always enabled. It is part of the baseline SDK behavior, not an integration option.

Get a session at action time

Call getSession() from an async context right before the sensitive action - not on app launch.
SessionHandoff is a small value type:
  • sessionId is stable for the life of the client.
  • getSession() posts the native snapshot, performs a bounded best-effort behavioral flush, and returns the handoff your backend should verify.
  • The SDK never surfaces verdicts, scores, or visitor IDs to the device - verify on your server.

Behavioral capture

When enableBehavioralSignals is true, the SDK starts zero-config UIKit capture automatically. It records the native equivalents of the browser’s behavioral events: app lifecycle, hashed screen navigation, viewport and scroll, form focus and input, selection and clipboard, and motion. No raw form text, raw placeholders, or raw field identifiers are sent. Field identity is hashed, and geometry is rounded to the field’s window coordinates so the dashboard can show timing and replay context without collecting user-entered values. Touch-stroke dynamics are still opt-in per screen for higher-fidelity gesture data. Call observeTouches(on:) once the view is mounted.
The observer attaches a non-consuming gesture recognizer, so your existing gestures keep working unchanged. In SwiftUI, reach for a UIViewRepresentable wrapper to expose the underlying UIView.

WebView correlation

If your app hosts Foil-protected web content in a WKWebView, attach it so the web SDK reuses the native session.
The native bridge exposes the session handoff as window.__FOIL_NATIVE__, which the browser SDK picks up when loaded inside the WebView.

Native identity and attestation

Native visitor continuity is resolved server-side from the SDK’s encrypted observations. install_id tracks the app install, device_id tracks the device continuity layer, and native visitor_id represents the same device within your organization. None of those identifiers are exposed to the app. Apple App Attest is optional and positive-only by default. When App Attest is supported, Foil can use SDK-managed App Attest keys to strengthen native identity. When it is unsupported, unavailable, unconfigured, or fails verification, Foil falls back to the other native continuity signals unless your organization explicitly enables strict attestation.
To verify App Attest in production, add your iOS app identity in the Foil dashboard. Baseline native continuity still works without this setup, but App Attest will not upgrade identity confidence until the app identity is configured.

Diagnostics

dispatchHealth() returns per-channel counters of successful and failed batch posts since the last configure(_:). Use it to confirm signals are reaching the server when you don’t have server-side visibility - for example, to spot a TLS-pinning, proxy, or quota issue from the client.
consecutiveFailures rising without a matching rise in consecutiveSuccesses typically means a sustained network outage or a server-side rejection that retries can’t recover from. Counters reset on configure(_:), destroy(), or resetLocalState().

API reference

Error handling

FoilError is a Sendable struct carrying a stable code, a human-readable message, an optional underlying error, and a retryable flag.
The code values are stable. Category prefixes (config.*, transport.*) are safe to branch on with a wildcard - new codes within a category keep the same retry semantics.

Fallback policy

If Foil fails, your app should still work. Log the error and continue without a handoff.
See Going to production for guidance on fall-open vs. fall-closed policy.

Best practices

  • Configure once at launch so signals are collecting before any screen opens.
  • Call getSession() late - right before the sensitive action, from an async context.
  • Degrade gracefully - never block the user if the SDK fails to produce a handoff.
  • Reuse the shared client - FoilClient.shared is a process-wide singleton.

What’s next

Server verification

Verify the sealed token on your backend

Browser SDK

Equivalent guide for the web surface

Android SDK

Native Android integration

Going to production

Rollout checklist and monitoring