getSession() when the user performs a sensitive action, and POST the sealed token to your backend for verification.
Requirements
- iOS 14+ for the public binary SwiftPM package
- Swift 5.9+
- Xcode 15+
- A Foil publishable key, starting with
pk_live_orpk_test_
Install
Add Foil with Swift Package Manager: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 yourApp 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
CallgetSession() from an async context right before the sensitive action - not on app launch.
SessionHandoff is a small value type:
sessionIdis 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
WhenenableBehavioralSignals 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.
UIViewRepresentable wrapper to expose the underlying UIView.
WebView correlation
If your app hosts Foil-protected web content in aWKWebView, attach it so the web SDK reuses the native session.
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.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.sharedis 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