# Other platforms

AppAttest ships official bridges for **React Native**, **Capacitor**, and **Flutter**, plus a CocoaPods option for native Swift. They wrap the same SDK and expose the same shape — `start`, `waitForReady`, read a secret, observe state — idiomatic to each runtime.

The account, secret, Apple Developer Console, and entitlements steps are identical across every platform. Do steps 1–4 of the [Quickstart](/docs/quickstart) first; this page only covers the per-platform **install** and **read** delta.

App Attest is an Apple capability, so these bridges gate and deliver secrets on the **iOS** side of your cross-platform app. There is no Android or web attestation path — on those targets the SDK is unavailable and your code should fall back accordingly.

Secrets are write-only on every platform: you set or overwrite a value in the dashboard, but the dashboard can never show a stored value back.

## React Native

```sh
npm install @appattest/react-native
cd ios && pod install
```

```ts
import { AppAttest, useSecret } from '@appattest/react-native';

// Once, at app launch:
AppAttest.start();

// In a component — re-renders when secrets land:
function ApiView() {
  const openaiKey = useSecret('OPENAI_API_KEY');
  return openaiKey ? <Ready /> : <Loading />;
}

// Outside components:
await AppAttest.waitForReady();
const key = await AppAttest.getSecret('OPENAI_API_KEY'); // string | null
const all = await AppAttest.getAllSecrets();             // Record<string, string>
```

Observe lifecycle state with the `useAppAttestState()` hook.

## Capacitor

```sh
npm install @appattest/capacitor
npx cap sync
```

```ts
import { AppAttest } from '@appattest/capacitor';

// Once, at app launch:
AppAttest.start();

// Anywhere:
await AppAttest.waitForReady();
const key = await AppAttest.getSecret('OPENAI_API_KEY'); // string | null
const all = await AppAttest.getAllSecrets();
```

Observe state with `await AppAttest.getState()` and `AppAttest.addStateListener(...)`.

## Flutter

```yaml
# pubspec.yaml
dependencies:
  appattest_flutter: ^0.1.0
```

```sh
flutter pub get
cd ios && pod install
```

```dart
import 'package:appattest_flutter/appattest_flutter.dart';

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

// Anywhere:
await AppAttest.waitForReady();
final key = await AppAttest.secret('OPENAI_API_KEY'); // String?
final all = await AppAttest.allSecrets();             // Map<String, String>
```

Observe state with `AppAttest.stateStream`.

## Swift via CocoaPods

If you use CocoaPods instead of Swift Package Manager:

```ruby
pod 'AppAttest'
```

Then use the SDK exactly as in the [Quickstart](/docs/quickstart#6-initialize-and-use). The Objective-C facade is available as `pod 'AppAttestObjC'` for bridge writers and Objective-C consumers; native Swift apps should depend on `AppAttest`. The bridges pull the iOS pods in automatically — you don't add them yourself.

## Testing without a device

App Attest needs real hardware. For simulator builds, previews, and tests, enable local stubs (one debug mode, stripped from release builds):

- **React Native / Capacitor:** `AppAttest.setDebugMode('local', { stubs: { OPENAI_API_KEY: 'sk-test-stub' } })`
- **Flutter:** `AppAttest.setDebugMode(DebugMode.local, { 'OPENAI_API_KEY': 'sk-test-stub' })`

## Errors

Every bridge surfaces the same stable error codes: `subscription_required`, `credits_required`, `attestation_rejected`, `service_unavailable`, `network`, `debug_mode_release_blocked`, `invalid_argument`. Gate your UI on the observed state rather than catching on the read path.

## What's next

- [Quickstart](/docs/quickstart) — the shared end-to-end flow (account, secrets, Apple Developer Console, entitlements).
- [Apple Developer Console setup](/docs/apple-developer-console) — enabling App Attest on your identifier.
- [Entitlements reference](/docs/entitlements) — what goes in the file and why.