3.7 KiB
@toolctrl/mobile — Capacitor 7 native wrapper
This workspace bundles the React Router v7 web app at apps/web/ as a
native iOS + Android shell via Capacitor 7.
It is not a separate UI codebase — there is exactly one product UI (the web app). This package only owns:
capacitor.config.ts— bundle id, web-dir, plugin configios/andandroid/native project skeletons (generated bycap add)src/auth.ts— mobile session persistence wrapper around@toolctrl/api-client-tssrc/sentry.ts— Sentry-Capacitor bootstrap (mirrorsapps/web/app/sentry.client.ts)src/pushRegistration.ts— APNs/FCM token registration against the backend- Generated app icons and splash screens (under
ios/andandroid/)
Status
Code scaffold + push backend is shipped under task #65 (Phase 08).
Actual native builds, Xcode signing, App-Store / Play-Store submission are
operator-owned tasks and are explicitly deferred — see
docs/operations/capacitor.md.
Local development
# 1. Install workspace deps (from repo root)
pnpm install
# 2. Build the web bundle that the native wrapper will load
pnpm --filter @toolctrl/mobile build:web
# 3. Sync web assets + plugins into the native projects
pnpm --filter @toolctrl/mobile sync
# 4. Open in Xcode / Android Studio
pnpm --filter @toolctrl/mobile open:ios
pnpm --filter @toolctrl/mobile open:android
Live reload (dev workflow)
For tight iteration loops point the wrapper at a running dev origin
instead of the bundled webDir:
export MOBILE_LIVE_RELOAD_URL=http://localhost:3000 # iOS simulator
# export MOBILE_LIVE_RELOAD_URL=http://10.0.2.2:3000 # Android emulator
pnpm --filter @toolctrl/mobile sync
This injects the URL into capacitor.config.ts > server.url. On the
emulator/simulator the app then loads from your dev box every reload.
Never set this for production builds.
Plugins
| Plugin | Purpose |
|---|---|
@capacitor/app |
App lifecycle + custom URL scheme handling |
@capacitor/browser |
In-app browser for the login flow |
@capacitor/status-bar |
Native status-bar theming |
@capacitor/splash-screen |
Splash on cold start |
@capacitor/push-notifications |
APNs + FCM token + display |
@capacitor/local-notifications |
Foreground reminders + UAT smoke |
@capacitor/preferences |
UserDefaults (iOS) / SharedPreferences (Android) session persistence |
@capacitor/preferences is not encrypted storage. Keep bearer material
short-lived here, or replace the auth storage adapter with a native
secure-storage plugin before persisting long-lived refresh tokens.
Why the native folders look minimal
When this scaffold was committed the agent did not have a working
local Xcode / Android-Studio install, so the iOS and Android projects
ship as the minimum subset required for cap sync to succeed:
apps/mobile/ios/App/App/Info.plist,App.xcodeproj/project.pbxproj, AppDelegate stubsapps/mobile/android/app/build.gradle,AndroidManifest.xml,MainActivity.java,settings.gradle
An operator with the native tooling installed should run
cd apps/mobile
rm -rf ios android
npx cap add ios
npx cap add android
npx cap sync
once to overwrite these skeletons with the canonical CLI output. Bundle
id, app name and webDir stay identical — Capacitor reads them from
capacitor.config.ts.
See also
docs/improvement-plan/08-mobile-cli-storybook-sdk/capacitor.mdfull spec + deviation logdocs/operations/capacitor.mdbuild / signing / submission runbook