Tip Track is a native SwiftUI iOS app for delivery workers to log orders, remember addresses, record tip ranges, and review tip patterns by location.
The original Next.js implementation remains in the repo for reference, but the iOS target no longer hosts a webview or Capacitor bridge. The native app lives in ios/App and starts directly from SwiftUI.
- Bundle ID:
com.steveafrost.tiptrack - Minimum iOS target: 15.0
- Entry point:
ios/App/App/AppDelegate.swift - UI framework: SwiftUI
- Address search: MapKit
MKLocalSearchCompleter - Persistence: Postgres through the mobile API when configured, with on-device
UserDefaultsas the development fallback
- Driver sign-in gate
- Add orders by order ID and address
- Native address autocomplete
- Search saved orders
- Edit order address and tip range
- Search saved locations
- View previous tips for a location
- Edit tips from the location history
- View report counts for each tip range
Open ios/App/App.xcodeproj in Xcode, select your Apple Developer Team, then build or archive the App target.
Command-line simulator build:
xcodebuild -project ios/App/App.xcodeproj \
-scheme App \
-configuration Debug \
-destination 'platform=iOS Simulator,OS=26.5,name=iPhone 17 Pro' \
-derivedDataPath /private/tmp/tip-track-derived \
buildThe Next.js app exposes native-friendly API routes under /api/mobile. They use the existing Prisma/Postgres Order and Location tables.
Set this on the deployed web/API app:
POSTGRES_PRISMA_URL=
POSTGRES_URL_NON_POOLING=The native app uses Sign in with Apple or Google. The backend verifies the provider identity token, resolves it to an internal TipTrack user, then stores that internal user id in Order.createdBy and AppStoreEntitlement.driverId.
Set these on the deployed web/API app:
MOBILE_SESSION_SECRET=
APPLE_SIGN_IN_AUDIENCE=com.steveafrost.tiptrack
GOOGLE_SIGN_IN_AUDIENCE=
MOBILE_REQUIRE_USER_AUTH=trueMOBILE_API_TOKEN is optional and still acts as an app-wide API shared secret. When it is set on Vercel, the native client must send the same bearer token in addition to the user session token.
Configure the native app in ios/App/App/Info.plist:
<key>TipTrackAPIBaseURL</key>
<string>https://usetiptrack.com</string>
<key>TipTrackAPIToken</key>
<string>optional same-value-as-MOBILE_API_TOKEN</string>
<key>GIDClientID</key>
<string>Google iOS client ID</string>
<key>GIDServerClientID</key>
<string>Optional Google web/server client ID</string>Set GOOGLE_SIGN_IN_AUDIENCE to the Google OAuth client id(s) expected in Google ID token aud claims. Multiple client ids can be comma-separated if iOS and web use different OAuth clients.
If TipTrackAPIBaseURL is blank, the iOS app stays local-only and persists data with UserDefaults.
POST /api/mobile/sessionGET /api/mobile/ordersPOST /api/mobile/ordersPATCH /api/mobile/orders/:externalIdGET /api/mobile/locations
When MOBILE_API_TOKEN is configured, all mobile API requests require:
Authorization: Bearer <MOBILE_API_TOKEN>
All order/location requests also require the signed user session returned by POST /api/mobile/session:
x-tip-track-session-token: <session-token>
The original Next.js UI can still be run for comparison:
pnpm install
pnpm devThe web app still requires:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
POSTGRES_PRISMA_URL=
POSTGRES_URL_NON_POOLING=
NEXT_PUBLIC_GOOGLE_MAPS_KEY=The /app web app uses Clerk OAuth for Apple or Google identity. Web API routes under /api/web resolve Clerk external accounts to the same internal TipTrack user model used by iOS. The browser no longer sends or controls a driver ID.
After deploying the auth schema, run the idempotent backfill once to convert known legacy owner keys like apple:<subject> into internal TipTrack user ids:
pnpm auth:backfillpnpm run build
pnpm exec tsc --noEmit
pnpm auth:backfill
swiftc -parse ios/App/App/AppDelegate.swift
plutil -lint ios/App/App.xcodeproj/project.pbxproj ios/App/App/Info.plist