Setup¶
Get the Tomoda frontend running on your machine for iOS, Android, and web.
Prerequisites¶
- Node.js 18+ (Expo SDK 55 baseline) and npm
- Git
- For iOS: Xcode 15+ with Command Line Tools and an iOS Simulator
- For Android: see the dedicated Android Setup page (JDK 17, Android Studio, SDK, emulator)
- (Optional) Expo CLI globally — not required;
npx expoworks fine
Expo Go vs. dev client
Tomoda has native dependencies (Google Maps, Google Sign-In, Apple Sign-In, Passkeys), so you cannot run it in plain Expo Go. Use npx expo run:ios / npx expo run:android to build a dev client, or run on web with npm run web.
Install¶
From the repo root:
# Either invoke the Task runner...
task deps:frontend
# ...or install directly
cd frontend
npm install
task deps:frontend is defined in Taskfile.yml and simply runs npm install inside frontend/.
Run¶
# Day-to-day: Metro + web; press i/a to attach the existing native dev binary
task dev:frontend # Metro for the installed dev binary
task dev:frontend:clear # Same, with Metro cache wiped
# First run / native dep change / broken build state
task dev:ios # builds + installs the iOS dev binary on the Simulator
task dev:ios:device # full prebuild + Pods + Xcode + Metro for a physical iPhone
task dev:ios:device:fast # re-attach to an already-installed device binary
task dev:android # builds + installs the Android dev binary on the emulator
Tomoda is a custom-native-modules app (Skia / MapLibre / Reanimated 4 / camera / view-shot etc.). Expo Go cannot run it — Metro always serves the dev client. After the first task dev:ios / task dev:ios:device finishes, daily work is task dev:frontend + pressing i in Metro (or relaunching the dev binary on the phone). Fast Refresh applies JS edits in <1 second; only native dep additions or app.config.js plugin changes need another full build.
The Expo dev server prints a QR code and a key menu:
| Key | Action |
|---|---|
w |
Open the web build in your default browser |
i |
Attach to the installed iOS dev binary on the Simulator |
a |
Attach to the installed Android dev binary on the emulator |
r |
Reload the app |
j |
Open the JS debugger |
You can also scan the QR with your phone if you have a development build installed.
Other tasks:
task build:frontend # static web export → dist/
task lint:frontend # expo lint + targeted eslint
task typecheck # tsc --noEmit across the monorepo
task test # full test suite
If you need the raw Expo CLI for a one-off (e.g. expo run:ios for a specific device build), cd frontend && npx expo … always works — Task is the wrapper, not a replacement.
Environment variables¶
The app reads two public env vars (Expo only exposes vars prefixed with EXPO_PUBLIC_):
| Variable | Purpose | Default |
|---|---|---|
EXPO_PUBLIC_API_URL |
REST base URL ({host}/api/v1) |
http://127.0.0.1:8080/api/v1 |
EXPO_PUBLIC_WS_URL |
WebSocket base URL | ws://127.0.0.1:8080/ws |
EXPO_PUBLIC_WEB_URL |
Public web origin (used for share links) | https://tomoda.life |
In production the values are baked into eas.json under the production profile:
"production": {
"env": {
"EXPO_PUBLIC_API_URL": "https://api.tomoda.life/api/v1",
"EXPO_PUBLIC_WS_URL": "wss://api.tomoda.life/ws"
}
}
Locally, drop them in frontend/.env (read by dotenv via app.config.js):
EXPO_PUBLIC_API_URL=http://127.0.0.1:8080/api/v1
EXPO_PUBLIC_WS_URL=ws://127.0.0.1:8080/ws
How the app finds the backend in dev¶
Expo Go and dev clients on different runtimes resolve localhost differently. Use the right loopback address for each target:
| Target | Loopback to use |
|---|---|
| Web (browser) | 127.0.0.1 or localhost |
| iOS Simulator | 127.0.0.1 |
| Android Emulator | 10.0.2.2 (special host route inside the AVD) |
| Physical device on LAN | Your Mac's LAN IP (e.g. 192.168.1.42) |
Android emulator + localhost
From the Android emulator, localhost resolves to the emulator itself, not the host machine. Always use 10.0.2.2 to reach the backend running on your Mac. If login fails immediately on Android, this is almost always the cause — see Android Setup.
First run checklist¶
- Backend running on port
8080(task dev:backend) .envset to the matching loopback for your targetnpm installcomplete infrontend/task dev:frontendrunning → presswori(aftertask dev:ioshas installed the dev binary)
If anything misbehaves, clear the Metro cache:
task dev:frontend:clear
Next steps¶
- Architecture — how the codebase is laid out
- Routing — Expo Router and the route tree
- State Management — the nine context providers