App Attest
The SDK authenticates itself. Every capture and data request is authorized by a bearer the SDK mints from an Apple App Attest assertion, scoped to the organisation you name. The device is attested against your App ID — which is why this cannot be pre-configured for you, and why the SDK carries no key of its own.
Four steps, in order. Skip any of them and device auth fails with auth(_:).
1. Get registered
Send us your app's bundle identifier and Team ID. You get back a
pk_ios_* publishable key per environment. The key is publishable — it ships in
your binary — but it is bound to the App ID and Team we register for App Attest.
For a key, a backend registration, or questions: info@thenoughtyfox.com.
2. Enable the App Attest capability
Add the App Attest capability to your app target under Signing & Capabilities, or enable DeviceCheck / App Attest on the App ID in the developer portal. Regenerate provisioning profiles afterwards.
3. Set the environment entitlement
<key>com.apple.developer.devicecheck.appattest-environment</key>
<string>development</string>
development uses Apple's App Attest sandbox; production uses the production
service. Keys created in one do not work in the other.
Builds delivered through TestFlight, the App Store, or the Enterprise Program
always attest against the production environment, whatever the entitlement
says. Only locally-signed development builds honour development.
4. Match the environment to the key
Configuration.environment picks the SDK backend, and each backend validates
attestations against one App Attest environment:
APIEnvironment | App Attest environment | Use for |
|---|---|---|
.development | sandbox | Locally-signed development builds |
.production | production | TestFlight, App Store, Enterprise |
A mismatch — a TestFlight build (production attestation) pointed at
.development, say — is rejected at the backend and reaches you as
attestationInvalid or serverError(statusCode:).
Pick the environment from your build configuration, and use the publishable key issued for that same environment:
let configuration = Configuration(
environment: isReleaseBuild ? .production : .development,
publishableKey: "pk_ios_…",
organizationProvider: authService,
settings: settingsStore
)
Establishing device auth
try await tours.prepareDeviceAuth()
Call it after login and organisation selection (and at launch for a restored
session), so a broken auth chain surfaces at a clean moment instead of on the
first sync. It is independent of start().
A retryable failure here costs nothing — sync re-validates before every run. A
non-retryable one (appAttestUnavailable, noOrganizationSelected, a rejected
key) will never resolve on its own and is worth surfacing.
Revoking on logout
try? await tours.revokeDeviceSession()
Ends the device session and drops the local credentials — even if the server-side revoke fails, so logout can always proceed.
Keep the same NoughtyTours instance: the organisation is read live through
your provider, so signing in as a different organisation needs no new one.