# Mobile Surfaces > The fastest way to ship Live Activities, Dynamic Island, widgets, and controls on iPhone. An Expo iOS reference architecture with a working harness, push SDK, contract types, and a 29-entry catalog of silent-failure traps. Requires iOS 17.2 or higher. ## Install `npm create mobile-surfaces` (also works with pnpm, yarn, bun) ## Repository https://github.com/glendonC/mobile-surfaces ## Full content The full CLAUDE.md trap catalog is mirrored at [/llms-full.txt](https://mobile-surfaces.com/llms-full.txt) so you can fetch the entire reference without following GitHub links. ## Documentation - [CLAUDE.md](https://github.com/glendonC/mobile-surfaces/blob/main/CLAUDE.md): Catalog of 29 silent-failure traps with fixes and enforcement scripts. Hard invariants for AI coding assistants. Read this first. - [Architecture](https://github.com/glendonC/mobile-surfaces/blob/main/docs/architecture.md): Adapter contract, surfaces, native constraints. - [Push](https://github.com/glendonC/mobile-surfaces/blob/main/docs/push.md): Wire layer, SDK reference, token taxonomy, error responses. - [Multi-surface](https://github.com/glendonC/mobile-surfaces/blob/main/docs/multi-surface.md): kind values (live-activity, widget, control, notification) and projection helpers. - [Troubleshooting](https://github.com/glendonC/mobile-surfaces/blob/main/docs/troubleshooting.md): Symptom-to-fix recipes. - [Backend integration](https://github.com/glendonC/mobile-surfaces/blob/main/docs/backend-integration.md): Domain event to snapshot to APNs walkthrough. ## Notable invariants for AI coding assistants When generating or editing code in a Mobile Surfaces project, treat every error rule in CLAUDE.md as a hard invariant. The catalog covers, among others: - MS001: Application code must import the live-activity adapter through the boundary re-export at apps/mobile/src/liveActivity, never directly from @mobile-surfaces/live-activity. - MS002: The two MobileSurfacesActivityAttributes.swift files (in packages/live-activity/ios/ and apps/mobile/targets/widget/) must be byte-identical. Drift causes ActivityKit to silently drop updates. - MS011: ActivityKit per-activity push payloads are bounded at 4 KB; iOS 18 broadcast pushes at 5 KB. Exceeding this returns 413 or silently drops. - MS012, MS027: iOS deployment target must be 17.2 or higher. Required for pushToStartTokenUpdates without if #available ceremony. - MS013, MS025: Host app and widget extension must declare the same com.apple.security.application-groups identifier in both app.json and the widget target's expo-target.config.js. Mismatch makes both sides read separate App Group containers. - MS014: APNs token environment must match the build environment. Development tokens cannot authenticate production endpoint and vice versa. - MS016: Subscribe to Activity<...>.pushToStartTokenUpdates at mount via liveActivityAdapter.addListener('onPushToStartToken', ...). getPushToStartToken() resolves null. - MS017, MS029: Generated apps/mobile/ios/ is gitignored and is regenerated by Continuous Native Generation. Manual edits are wiped on the next prebuild. - MS018: APNS_BUNDLE_ID must not include the .push-type.liveactivity suffix. The SDK auto-appends it. - MS028: APNs auth env vars (APNS_KEY_ID, APNS_TEAM_ID, APNS_KEY_PATH, APNS_BUNDLE_ID) must all be set before sending. For the full list with detection method, severity, fix, and enforcement script, read CLAUDE.md. ## Tech stack - Expo SDK with Continuous Native Generation - React Native - TypeScript - Zod (contract validation) - @bacons/apple-targets (widget target management; pin exact version) - iOS 17.2 or higher (push-to-start tokens require this floor)