Device Setup — EAS Build + OTA Updates
Superseded (2026-07): folded into Builds & Deployment. Kept for reference.
Status: Superseded
How to get TwoMore v2 running on a physical device with over-the-air (OTA) updates wired up.
One-time setup
1. Install deps
yarn installThis installs eas-cli as a dev dep (already added to root package.json).
1b. Environment variables
cp .env.example .env
ln -sf ../../.env apps/mobile/.envWhy the symlink? Expo CLI loads env vars from the project root (where app.json lives = apps/mobile/), not from the monorepo root. The symlink keeps a single source of truth so we don't have two .env files drifting apart. .gitignore already ignores .env anywhere in the tree.
Edit .env and fill in at minimum:
EXPO_PUBLIC_SUPABASE_URL— your Supabase project URL (separate from v1's)EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY— Supabase anon key
Without these the app won't connect to the backend. Get them from Supabase Dashboard → Settings → API.
1c. Create a test user
In the Supabase Dashboard → Authentication → Users → Add user, create an email+password account. You'll use it to sign into the app on device. (Sign-up isn't in the app UI yet — Phase 8c.)
2. Log in to Expo
npx eas login(account: lbsky — the one used for v1)
3. Create the v2 EAS project
yarn eas:initThis runs eas project:init inside apps/mobile/. It creates a new EAS project (separate from v1's) and inserts extra.eas.projectId into apps/mobile/app.json.
Then wire the OTA updates URL:
cd apps/mobile && npx eas update:configureThis adds updates.url pointing at your new project's Expo updates server, using the projectId from the previous step.
Commit both changes to app.json.
4. Configure native credentials (iOS + Android)
cd apps/mobile
npx eas credentialsSet up iOS push certs, Android keystore, etc. Only needed when you first build native.
5. First development build
yarn build:devProduces a development client APK/IPA. Install it once per device — it contains the native code. After that, JS changes ship via OTA without rebuilding.
Daily workflow
Test changes locally
yarn mobileExpo dev server starts. Connect the dev client on device to reload.
Deploy JS changes via OTA
yarn ota:previewRuns eas update --branch preview --environment preview. Devices on the preview channel auto-fetch the update next launch.
Deploy a new native build (when you add a native module)
yarn build:previewAPK/IPA for internal testing.
For store-ready builds, run from the mobile app directory so the production profile in apps/mobile/eas.json is used:
cd apps/mobile && eas build --profile productionWhen to build native vs OTA
OTA (no rebuild):
- JS/TS changes
- Tamagui styling
- Pure component work
- Feature logic in
packages/* - New Supabase queries
Native rebuild required:
- Adding/removing native modules (e.g.,
@react-native-kakao/user, new payment SDK) - Changing
app.jsonplugins array - Bumping
expoSDK version - Changing bundle identifier / package name
- Adding custom native code
Rule of thumb: if yarn install adds something that ships native code, rebuild. Otherwise OTA.
Native dep preflight (prevents cloud-build failures)
Yarn workspaces do not auto-install peer dependencies, and Expo SDKs pin specific versions of native modules (reanimated, gesture-handler, svg, worklets). A drift either produces a Gradle failure in the cloud ("Process 'command 'node'' finished with non-zero exit value 1") or a runtime crash.
Every yarn build:dev and yarn build:preview runs yarn preflight (= expo install --check) first. If it reports drift, run yarn expo install <pkg>@<expected-version> inside apps/mobile/ before retrying the build. Never bypass.
When bumping reanimated across a major version, also verify the babel plugin path. Reanimated v4 moved it from react-native-reanimated/plugin → react-native-worklets/plugin — see apps/mobile/babel.config.js.
Troubleshooting
"No EAS project configured": run yarn eas:init.
"Process 'command 'node'' finished with non-zero exit value 1" in :react-native-reanimated: the react-native-worklets peer dep is missing or mismatched. Run yarn preflight to diagnose.
"Fonts not loading": verify .otf files exist in assets/fonts/. Pretendard fonts are committed to the repo (per v1 convention).
"Styles are missing after OTA": Tamagui compiler output depends on your tokens.config — if tokens changed, a native rebuild is required since tamagui-web.css is baked at build time.
"Update shipped but device doesn't pick it up": close and reopen the app twice. expo-updates fetches on launch, applies on the next one. Or run npx expo update --clear-cache.
Channels
| Channel | Purpose |
|---|---|
development | Dev client builds, live reload from Metro |
preview | Internal testing builds, OTA updates for QA |
production | Store builds, OTA updates for end users |