Local Development¶
How to run the native iOS and Android apps locally without going through EAS. Use this when you need fast iteration — code change to running app in seconds rather than minutes, with no EAS build queue.
Three local-dev paths exist:
- Expo Go — covered in Frontend → Setup. Quickest start; works for everything that doesn't need native modules beyond Expo's standard set.
expo run:android/expo run:ios— native development build on simulator/emulator. Required when you've added a native module that Expo Go doesn't include (Google Sign-In, native maps with Google Maps SDK, etc.).- EAS dev client — a custom dev binary installed once, then JS bundle hot-reloads against it. Best for ongoing native-module work without rebuilding the binary each time.
This page covers paths 2 and 3 on macOS, since Tomoda's primary native dev surface is Android (more common configuration friction). iOS local dev is shorter — mostly Xcode + a single command — and gets a brief section at the end.
Android — toolchain¶
You need three things working in concert: Java, the Android SDK, and an emulator (or a tethered physical device).
| Tool | Required version | How to install |
|---|---|---|
| JDK | OpenJDK 17 | brew install openjdk@17 |
| Android Studio | Latest stable | Download |
| Android SDK Platform-Tools | Latest | Android Studio → SDK Manager → SDK Tools tab |
| Android Emulator + a system image | Latest | Android Studio → Virtual Device Manager |
| Node | 18+ | brew install node |
Wire JDK 17 into the system¶
brew install puts the binaries in place but doesn't make them the system default. Symlink it:
sudo ln -sfn /usr/local/opt/openjdk@17/libexec/openjdk.jdk \
/Library/Java/JavaVirtualMachines/openjdk-17.jdk
Verify:
java -version
# openjdk version "17.0.x"
Set environment variables¶
Add to ~/.zshrc (or ~/.bashrc):
# Java
export JAVA_HOME="/usr/local/opt/openjdk@17"
# Android SDK — default Mac install location
export ANDROID_HOME="$HOME/Library/Android/sdk"
# CLI tools on PATH
export PATH="$ANDROID_HOME/emulator:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools:$PATH"
Reload your shell: source ~/.zshrc.
M2 Macs running Rosetta Homebrew
If brew --prefix openjdk@17 returns a /usr/local/... path rather than /opt/homebrew/..., your Homebrew is installed under Rosetta (Intel-compatible mode). This is fine — the paths above already match. If you migrated to a native Apple Silicon Homebrew, swap /usr/local for /opt/homebrew everywhere.
Create an emulator¶
Android Studio → Tools → Device Manager → Create Virtual Device.
| Setting | Pick |
|---|---|
| Device | Pixel 9 (or any reasonably recent phone profile) |
| System image | API 35 (Android 15) |
| Graphics | Hardware - GLES 2.0 |
Boot it once before running anything else — first boot can take 60+ seconds.
Android — running the app¶
From the repo root:
cd frontend
npx expo run:android
What this does:
- Checks for a connected Android device or running emulator. If none is found, prints an error.
- Compiles the native Android code (Gradle build — takes a few minutes the first time, faster on subsequent runs).
- Installs the APK on the device.
- Starts Metro and connects.
Pointing the app at your local backend¶
The Android emulator runs in its own VM — localhost from inside the emulator is the emulator itself, not your Mac. Use the loopback address 10.0.2.2 to reach services running on the host:
EXPO_PUBLIC_API_URL=http://10.0.2.2:8080/api/v1 npx expo run:android
A physical tethered device sees your Mac directly via its LAN IP (192.168.1.x etc.).
Choose a specific target¶
# List devices visible to ADB
adb devices
# Build for a specific device id
npx expo run:android --device <id>
# Build a release variant locally (signed with the debug keystore)
npx expo run:android --variant release
Reset Metro cache when things get weird¶
rm -rf $TMPDIR/metro-* $TMPDIR/haste-map-*
npx expo start --clear
Android — Google Maps integration¶
The map screens use the Google Maps SDK for Android, which is gated on a SHA-1 fingerprint registered in the Google Cloud Console. Without this, map tiles render as a gray grid.
Get the debug keystore SHA-1¶
keytool -list -v -keystore ~/.android/debug.keystore \
-alias androiddebugkey \
-storepass android \
-keypass android \
2>/dev/null \
| grep 'SHA1:'
You'll get something like:
SHA1: 5E:8F:16:06:2E:A3:CD:2C:4A:0D:54:78:76:BA:A6:F3:8C:AB:F6:25
Register it in Google Cloud Console¶
- GCP Console → APIs & Services → Credentials (in project
development-485000). - Open the Maps SDK for Android API key.
- Application restrictions → Android apps → add an entry:
- Package name:
com.tomoda.app - SHA-1 fingerprint: paste the value above.
- Package name:
- Save. Map tiles should render on next app reload (allow a minute for propagation).
Each developer registers their own debug SHA-1. The production SHA-1 (used for Play-signed builds) is registered separately by whoever runs the release pipeline — see Play Store.
iOS — toolchain¶
iOS local dev is shorter because Apple's signing complexity is managed by EAS Credentials.
| Tool | Required | How |
|---|---|---|
| Xcode | Latest stable | App Store |
| Xcode command-line tools | Bundled | xcode-select --install once after Xcode |
| CocoaPods | Latest | sudo gem install cocoapods |
| iOS Simulator | Bundled with Xcode | Open Xcode → Settings → Platforms → install a recent iOS version |
iOS — running the app¶
cd frontend
npx expo run:ios
This launches the iOS Simulator (default device — usually iPhone 15 Pro), runs pod install if needed, builds the Xcode project, installs the app, and connects Metro.
Choose a specific simulator¶
npx expo run:ios --device # interactive picker
Local backend from the simulator¶
The iOS Simulator shares the host's network — http://127.0.0.1:8080 works as-is (no loopback gymnastics like Android).
EAS dev client (recommended for ongoing native work)¶
If you're going to be developing against native modules over multiple sessions, build an EAS dev client once and use it instead of repeating expo run:android / expo run:ios:
eas build --profile development --platform android # or ios
Install the resulting binary onto your device/emulator. Then run npx expo start and connect — your JS bundle hot-reloads against the dev client's native runtime without rebuilding the binary.
See Native Testing for using dev clients in QA workflows.
Common pitfalls¶
- "No connected devices" — open Android Studio → Device Manager → Play your emulator manually before running
expo run:android. Same for iOS — make sure the Simulator is open. - "Task 'installDebug' not found" — you're not in the
frontend/folder.cd frontendfirst. - Network error on login (Android) — emulator can't reach
localhost. Use10.0.2.2for the API URL. - Map tiles render as gray — debug keystore SHA-1 not registered in Cloud Console. See the Google Maps section above.
- Pods complaining on iOS after dependency change —
cd frontend/ios && pod install. Or deleteios/Podsandios/Podfile.lockand re-runnpx expo run:ios. - JDK mismatch on Android Gradle build — confirm
java -versionshows 17, not 11 or 21.
What this page does NOT cover¶
- The release pipeline — see Native Release for the EAS Build/Submit flow.
- Store portal management — see Play Store for Google Play Console, App Store for App Store Connect.
- Testing strategy — see Native Testing for emulator/device QA flows and OAuth caveats.