GenerateSaaS

Setup

Install the native toolchains, run the mobile app on a simulator or a device, and set the EXPO_PUBLIC_* variables it reads.

Running apps/mobile needs the native toolchains Expo builds against. There is no Expo Go path - the app uses native modules, so the first run compiles a development build.

Prerequisites

PlatformInstallNotes
iOSXcode with its Command Line ToolsmacOS only. Open Xcode once to accept the license and install a simulator runtime.
AndroidAndroid Studio, one SDK platform, one emulator imageSet ANDROID_HOME and put platform-tools on your PATH.
AndroidJDK 17Android Studio ships one; java -version must report 17.
BothNode 22.18+ and pnpmThe repo's own engines.node.

First run

Install the workspace from the repo root: pnpm install.
Copy apps/mobile/.env.example to apps/mobile/.env and fill it. Expo reads env from its OWN project root, never the monorepo root.
Compile and launch the development build: pnpm --filter mobile exec expo run:ios (or expo run:android). The first build takes minutes; later runs reuse it.
Start Metro for every run after that: pnpm --filter mobile mobile:start.

Sentry needs nothing before the first build. The Sentry Expo plugin is added only when SENTRY_ORG and SENTRY_PROJECT are both set, so a fresh clone compiles with no symbol-upload build phase to fail on. Set both, plus SENTRY_AUTH_TOKEN, in apps/mobile/.env or your shell when you want source maps and debug symbols uploaded. Expo reads that file before it evaluates app.config.ts, and none of the three is an EXPO_PUBLIC_* key, so none is inlined into the bundle. Runtime error reporting is separate and rides EXPO_PUBLIC_SENTRY_DSN either way.

localhost is the device's loopback, not yours. An Android emulator reaches your machine at 10.0.2.2; a physical device needs your machine's LAN IP. Change EXPO_PUBLIC_API_URL and EXPO_PUBLIC_WEB_URL to match, or every request fails with a network error.

Environment

apps/mobile/.env and .env.example are written by the CLI. Every EXPO_PUBLIC_* value is inlined into the bundle, so none is ever a secret.

VariablePurpose
EXPO_PUBLIC_API_URLAbsolute API base. Fullstack: https://host/api. Separate backend: https://api-host. Fill it first: without it the app throws at launch and names it.
EXPO_PUBLIC_WEB_URLWeb origin for legal pages and web-only flows.
EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID, EXPO_PUBLIC_GOOGLE_IOS_CLIENT_IDNative Google sign-in.
EXPO_PUBLIC_SENTRY_DSN, EXPO_PUBLIC_POSTHOG_KEYObservability. Optional.
EAS_PROJECT_IDRead by app.config.ts in Node, never inlined, and not an EXPO_PUBLIC_* app key. Set it in your shell and the EAS environments locally, and as a GitHub repository variable - not a secret - for the release workflow to read.

apps/mobile/types/env.d.ts names every EXPO_PUBLIC_* key, and tests/env-contract.test.ts holds it and .env.example to the same set both ways. The backend's own server keys are on Environment variables.

On a physical device

Sign in to EAS: pnpm --filter mobile exec eas login. The workspace installs the eas-cli version eas.json requires, so never use a global one.
On iOS, register the device once: pnpm --filter mobile exec eas device:create.
Build the device profile: pnpm --filter mobile exec eas build --profile development-device --platform ios (or android).
Install the artifact from the EAS build page, then run pnpm --filter mobile mobile:start and open the app.

Use development-device, not development: the latter sets ios.simulator, so its iOS artifact runs on a simulator and cannot be installed on a phone. Both carry the same dev client and the same development channel. A development build is development-signed, so iOS 16 and later asks for Developer Mode on the phone (Settings -> Privacy & Security). See Release.

On this page