Xcode setup
This page covers the Xcode side of setup: adding the App Attest capability, adding the Swift package, and pointing the client at your project. The Apple Developer Console steps come first — see Apple Developer Console setup.
1. Add the App Attest capability
- Open your project in Xcode.
- Select the project in the Navigator (top item, blue icon).
- Select your app target from the TARGETS list.
- Click the Signing & Capabilities tab.
- Click + Capability at the top of the capability list.
- In the search field, type App Attest.
- Double-click App Attest to add it.
Xcode does two things:
- Writes an entry to your app target’s entitlements file. If you don’t have one yet, Xcode creates
YourAppName.entitlementsnext to your source. - Updates the target’s build settings to point at that entitlements file.
See entitlements for the exact contents.
2. Check signing
In the same Signing & Capabilities tab:
- Automatically manage signing should be checked unless you have a reason to manage profiles manually.
- The Team dropdown should show the team that owns the identifier you enabled App Attest on.
- Bundle Identifier should match that identifier exactly.
Build once. Xcode will regenerate the provisioning profile if needed. If you see a signing error, Xcode usually shows a Try Again button that fixes it.
3. Add the AppAttest Swift package
- File → Add Package Dependencies.
- In the search field at the top right, paste:
https://github.com/AppAttest/sdk. - Wait for Xcode to resolve it.
- Dependency Rule: Up to Next Major Version, from the latest tagged release.
- Click Add Package.
- In the product selection dialog, add the AppAttest product to your app target. Do not add it to test targets.
Xcode adds the package to your project’s Package Dependencies list and links it into the app.
4. Wire the SDK into @main
No configuration is required. The SDK detects your bundle identifier and Apple Team ID from the running process — there is no dashboard key or token to paste in. One call in your App init runs the whole bootstrap:
import SwiftUI
import AppAttest
@main
struct MyApp: App {
init() { AppAttest.start() }
var body: some Scene {
WindowGroup { ContentView() }
}
}
Your app is identified by (Team ID, bundle ID) at the moment the SDK runs its first attestation. Production secrets only reach production-signed builds; sandbox secrets only reach development builds. The environment is inferred from the App Attest entitlement you added in step 1.
AppAttest.start() is synchronous and idempotent; the actual attest + sync run on a background Task. Cold-start reads after the first launch hydrate from Keychain before the first frame.
5. Read a secret
Access secrets anywhere via the synchronous subscript on the static AppAttest namespace:
struct ContentView: View {
var body: some View {
if let key = AppAttest.secrets["OPENAI_API_KEY"] {
Text("Ready")
} else {
ProgressView("Loading…")
}
}
}
AppAttest.secrets is observed by SwiftUI through AppAttestClient.shared (which is @Observable @MainActor). When a value lands or rotates, any view reading it re-renders. The subscript returns String? — nil while the first sync is still running, the value once it lands.
For unhappy paths, switch on AppAttest.state:
struct RootView: View {
@Environment(AppAttestClient.self) private var attest
var body: some View {
switch attest.state {
case .initializing, .attesting, .syncing:
SplashView()
case .ready:
MainView()
case .subscriptionRequired(let err):
UnavailableView(title: "Service paused", error: err)
case .creditsRequired(let err):
UnavailableView(title: "Service paused", error: err)
case .unavailable(let err):
RetryView(error: err) { attest.retry() }
}
}
}
Troubleshooting
”Missing com.apple.developer.devicecheck.appattest-environment”
The entitlement was not added to the build. Re-check Signing & Capabilities. The App Attest capability should be present. If it is, delete DerivedData and build again:
- Xcode → Settings → Locations → Derived Data. Click the arrow to open it in Finder.
- Quit Xcode.
- Delete the
DerivedDatafolder. - Reopen and build.
”App Attest not supported on this device”
You are on a simulator, or the device is older than iPhone 6s / iOS 14. For simulator development:
#if DEBUG
AppAttest.debugMode = .sandbox
#endif
AppAttest.start()
.sandbox hits real network but skips Apple attestation, and only returns development-environment secrets. For offline tests, use .local(stubs: [...]) — for example:
#if DEBUG
AppAttest.debugMode = .local(stubs: [
"OPENAI_API_KEY": "sk-test-stub",
"BACKEND_KEY": "dev-token-abc"
])
#endif
AppAttest.start()
Both modes are #if DEBUG-gated and physically absent from Release builds.
”Attestation failed” at runtime
The bundle identifier, team, or App Attest capability on the Apple side is misaligned with what AppAttest expects. Check:
- Dashboard bundle ID matches Xcode bundle ID exactly.
- Apple Developer Console shows App Attest enabled on that identifier.
- You are running a signed build, not an ad-hoc export.
Full error reference: https://docs.appattest.dev/errors/attestation.
What’s next
- Entitlements reference — the exact file contents Xcode writes.
- For AI agents — how to help a user complete this setup.