Mobile Dev Build & Device Install
How to get a working build of the mobile app onto a real device or emulator during development — the day-to-day loop, distinct from the two standalone paths documented elsewhere (see References).
When to use
- You changed native code, added a native module, or bumped an Expo SDK/plugin,
and Metro-only (
npx expo startagainst Expo Go) is not enough or not applicable. - You need the app installed on a physical device or emulator to iterate with live JS reload.
adb installfails, the app crashes on launch, or the device never shows up as a build target.
Diagnosis
The mobile app is an Expo CNG (Continuous Native Generation) project:
mobile/android and mobile/ios are generated on demand by expo prebuild
and gitignored (mobile/.gitignore: /android, /ios). There is no committed
native project to open in Android Studio directly.
@maplibre/maplibre-react-native and expo-secure-store are native
modules — they do not run inside Expo Go. Any change under mobile/ needs a
development build (npx expo run:android), not the Expo Go app.
Check the toolchain and target before building:
node -v # Node 20+
java -version # JDK 17
adb devices # at least one device/emulator, state "device" not "unauthorized"
A device listed as unauthorized means the "Allow USB debugging" prompt on the
phone was not accepted (or was accepted for a different machine's RSA key) —
unlock the phone and accept the prompt, then re-run adb devices.
Procedure
-
Install dependencies once, from the repo root (links
@btp/corevia npm workspaces — see ADR-053):npm install -
Point the app at a reachable API (see
mobile/README.md, "Pointing at the API").EXPO_PUBLIC_API_URLhas no default:app.config.jsrefuses to resolve and the app throws at startup without it. Createmobile/.envbefore building:EXPO_PUBLIC_API_URL=https://<your-tunnel>.ngrok-free.dev EXPO_PUBLIC_WEB_URL=https://<your-tunnel>.ngrok-free.dev EXPO_PUBLIC_CONTACT_EMAIL=contact@bike-trip-planner.comThe host must be reachable from the phone (
localhostnever is).EXPO_PUBLIC_*values are inlined at bundle time: restart Metro after changing them. -
Build the dev client and install it on the connected device/emulator:
cd mobile npx expo run:androidThis runs
expo prebuild(regeneratingandroid/), compiles the native project, and installs the resulting debug APK — Metro serves the JS bundle, nothing is baked in. -
Subsequent JS-only changes don't need a rebuild — just restart the Metro server against the already-installed dev client:
npx expo start --dev-client -
Reinstall without rebuilding (e.g. after
adb uninstall, or to move an already-built APK to a second device):adb install -r android/app/build/outputs/apk/debug/app-debug.apk
Verification and follow-up
- Confirm the app opens to the login screen and Metro's terminal shows the device connected ("Android Bundled ... " logs streaming).
-
Exercise the auth deep link once to confirm the build is wired correctly (see
mobile/README.md, "Testing the deep link"):adb shell am start -a android.intent.action.VIEW \ -d "biketripplanner://auth/verify#TEST_TOKEN" -
If you only needed a build to hand off or sideload without keeping Metro running, use the preview path instead (
npm run build:preview— seemobile/README.md, "Installable preview build") rather than repeating this runbook per install.
References
mobile/README.md— full prerequisites, API pointing, deep-link testing, assetlinks- mobile-release-build.md — release-signed standalone APK (a different, later-stage build than this one)
- ADR-053 — npm-workspaces monorepo (
core/pwa/mobile)