Files
soconnor 355b14faef Add local iOS release pipeline, fix shortcuts, and improve invoice UX.
Enable App Store builds without EAS, iOS 18 App Intents plugins, and signing
fixes for distribution export. Add mobile invoice PDF preview, compact line
items, and more reliable shortcut deep-link handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 01:08:20 -04:00

4.8 KiB

beenvoice Mobile

Expo companion for beenvoice — dashboard, time clock, invoices, clients, businesses, and settings. Shares the same tRPC API and better-auth sessions as the web app.

Architecture (dense): docs/ARCHITECTURE.md

Prerequisites

  • Bun 1.3+
  • beenvoice API running (setup)
  • Xcode + iOS Simulator (or device) for native dev build
  • Not Expo Go — widgets, SecureStore auth, and biometrics need expo-dev-client

Setup

cd beenvoice-app
bun install
cp .env.example .env

.env:

# Simulator
EXPO_PUBLIC_API_URL=http://localhost:3000

# Physical iPhone — Mac LAN IP
EXPO_PUBLIC_API_URL=http://192.168.1.42:3000

Omit EXPO_PUBLIC_API_URL in production builds to default to https://beenvoice.soconnor.dev.

Server must enable @better-auth/expo in beenvoice/src/lib/auth.ts with beenvoice:// in trustedOrigins.

Run

# Terminal 1 — API
cd ../beenvoice && bun run dev

# Terminal 2 — mobile (builds native app if needed)
cd beenvoice-app && bun run ios

Metro uses port 8082 (avoids other Expo projects on 8081).

Metro only (app already installed):

bun run start -- --clear

Open the beenvoice dev build on the simulator — not Expo Go.

After native changes

Icon (assets/beenvoice.icon), widgets, or new native modules:

bunx expo prebuild --platform ios --clean
bun run ios

Features

Area Details
Auth Sign in, register, forgot/reset password; official or self-hosted server
Multi-account Bitwarden-style switcher; per-account session in SecureStore
Dashboard Revenue, pending, overdue, running timer, recent invoices
Timer Clock in/out, client + invoice + rate; optional description (default "Clock In"); iOS Live Activity
Entities Clients and businesses — list, create, edit
Invoices List, filter, create, edit, status updates
Settings Profile, accounts, theme, per-account app lock (PIN + Face ID), sign out
App lock Per-account; locks on background return

Auth & accounts (summary)

  • Guest auth storage: beenvoice:guest until first successful login
  • Per account: beenvoice:auth:{host::userId} in SecureStore
  • After login, finalizeAuthenticatedAccount() migrates session keys before activating the account (avoids double login)
  • Server picker: Official (beenvoice.soconnor.dev) or custom URL on auth screens

Full flow: docs/ARCHITECTURE.md#multi-account-model

URL Action
beenvoice://reset-password?token=… Reset password
beenvoice://timer Open time clock
beenvoice://shortcuts/clock-in Clock in (last client)
beenvoice://shortcuts/clock-in?title=… Clock in with title
beenvoice://shortcuts/clock-out Clock out running timer

iOS Shortcuts / Siri (requires native rebuild: bunx expo prebuild --platform ios && bun run ios):

  • Clock In — starts the timer with your last client
  • Clock Out — stops the running timer
  • Open Time Clock — opens the timer tab
  1. Install a fresh build on a physical iPhone (iOS 16+).
  2. Open the app once while signed in (registers shortcuts with the system).
  3. Shortcuts app → search beenvoice → add actions, or say “Hey Siri, clock in with beenvoice”.
  4. Pick a client once on the Timer tab before the first clock-in shortcut.

Test deep links:

xcrun simctl openurl booted "beenvoice://shortcuts/clock-in"
xcrun simctl openurl booted "beenvoice://shortcuts/clock-out"
xcrun simctl openurl booted "beenvoice://timer"

Project layout

app/
  _layout.tsx           # Providers, auth guard
  (auth)/               # sign-in, register, password flows
  (app)/                # tab shell + nested stacks
components/             # UI, forms, chrome, time clock
contexts/               # Auth, Accounts, AppLock, Theme
lib/                    # tRPC, auth storage, config, theming
widgets/                # iOS Live Activity (TimeClockActivity)

Troubleshooting

Issue Fix
PlatformConstants / runtime not ready Stop other Metro on 8081/8082; rebuild with prebuild --clean
Expo Go Use bun run ios dev build
API errors on device EXPO_PUBLIC_API_URL = Mac LAN IP; server BETTER_AUTH_URL must match
Live Activity empty Rebuild iOS; widget UI must live inside "widget" function
Login twice Server + app versions with session migration (lib/auth-storage.ts)