# Get started with AppAttest

Deliver your API keys to genuine iOS installs at runtime. Add the SDK, store your secrets in the dashboard, and read them in code — nothing sensitive ships in your binary.

## Overview

Every platform exposes the same shape: `start` → optionally `waitForReady` → read a secret → observe lifecycle state. There's no configuration: the SDK derives your app identity on-device, and sandbox vs production is selected automatically from Apple's attestation.

## Requirements

- iOS 17 or later.
- A real device — App Attest needs hardware, so there's no simulator path. Use debug mode for previews and CI.
- Swift natively, or React Native, Flutter, or Capacitor via the bridges. The bridges deliver on the iOS side; there's no Android or web attestation.

## Install & quickstart

The same shape on every platform — start once at launch, then read a secret.

### Swift (Swift Package Manager)

```swift
// Package.swift — or with CocoaPods: pod 'AppAttest'
.package(url: "https://github.com/AppAttest/appAttest-sdk.git", from: "0.1.0")

import SwiftUI
import AppAttest

@main struct MyApp: App {
  init() { AppAttest.start() }
  var body: some Scene { WindowGroup { ContentView() } }
}

// read — synchronous, nil until the first sync lands
let key = AppAttest.secrets["OPENAI_API_KEY"]
```

### React Native (npm)

```ts
// npm install @appattest/react-native && cd ios && pod install
import { AppAttest, useSecret } from '@appattest/react-native'

AppAttest.start()                              // start once, at launch
const key = useSecret('OPENAI_API_KEY')        // in a component — re-renders when secrets land
const k = await AppAttest.getSecret('OPENAI_API_KEY') // outside components — async
```

### Flutter (pub.dev)

```dart
// pubspec.yaml: appattest_flutter: ^0.1.0  → flutter pub get && cd ios && pod install
import 'package:appattest_flutter/appattest_flutter.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  AppAttest.start();
  runApp(const MyApp());
}

final key = await AppAttest.secret('OPENAI_API_KEY'); // read — async
```

### Capacitor (npm)

```ts
// npm install @appattest/capacitor && npx cap sync
import { AppAttest } from '@appattest/capacitor'

AppAttest.start()                                     // start once, at launch
const key = await AppAttest.getSecret('OPENAI_API_KEY') // read — async
```

## Reading secrets

Swift reads are a synchronous subscript. The cross-platform bridges read asynchronously — that difference is intentional per runtime.

- **Swift** — `let key = AppAttest.secrets["KEY"]`
- **RN / Capacitor** — `await AppAttest.getSecret('KEY')`
- **Flutter** — `await AppAttest.secret('KEY')`

For a required key, await `waitForReady()` before reading so the first sync has landed.

## Lifecycle state

Gate your UI on state rather than try/catch. The states are: `ready`, `initializing`, `attesting`, `syncing`, `subscriptionRequired`, `creditsRequired`, `unavailable`.

In SwiftUI the client is observable, so views re-render as the state changes:

```swift
switch AppAttestClient.shared.state {
case .ready:                                   // secrets are available — use them
case .subscriptionRequired:                    // send the developer to subscribe
case .creditsRequired:                         // prompt a top-up
case .unavailable:                             // transient — show a retry and call retry()
case .initializing, .attesting, .syncing:      // loading — show a spinner
}
```

On the bridges the same states arrive via `useAppAttestState()` (React Native), `stateStream` (Flutter), and `getState()` + listeners (Capacitor).

## Testing without a device

App Attest needs real hardware, so it can't run in the simulator, in SwiftUI previews, or in CI. Inject local stubs in debug — your code reads secrets exactly as it will in production, and the whole app runs normally. The stubs are stripped from release.

```swift
#if DEBUG
AppAttest.debugMode = .local(stubs: ["OPENAI_API_KEY": "sk-test-stub"])
#endif
AppAttest.start()
```

## Go deeper

- [Other platforms](/docs/other-platforms.md) — Install and use AppAttest from React Native, Capacitor, or Flutter. Same write-only secrets, same App Attest gating, idiomatic per-runtime APIs.
- [Apple Developer Console setup](/docs/apple-developer-console.md) — Enable App Attest on your app identifier in Apple Developer. Step-by-step with exact menu paths.
- [Xcode setup](/docs/xcode-setup.md) — Add the App Attest capability and the AppAttest Swift package to your Xcode project.
- [Entitlements reference](/docs/entitlements.md) — The exact entitlement key App Attest uses, what values are valid, and how environments map to your AppAttest dashboard.
- [For AI agents](/docs/agents.md) — How an AI agent should read AppAttest docs on a developer's behalf, and how we expose markdown on every page for that purpose.
