Warning
T3 Code Mobile is currently in development and is not distributed yet. If you want to try it out, you can build it from source.
Note
Uses native modules so using Expo Go is not supported. You need to use the Expo Dev Client.
This app has three variants:
development: Expo dev client, installable side-by-side asT3 Code Devpreview: persistent internal preview build, installable side-by-side asT3 Code Previewproduction: store/release build asT3 Code
Run commands from apps/mobile.
T3 Connect is optional and disabled in a fresh clone. Public configuration belongs in the
repository-root .env or .env.local, not an apps/mobile/.env file. See
../../.env.example.
To sign a fork with your own Apple Developer account, set
T3CODE_MOBILE_IOS_TEAM_ID, T3CODE_MOBILE_IOS_BUNDLE_IDENTIFIER,
T3CODE_MOBILE_EAS_PROJECT_ID, and T3CODE_MOBILE_EXPO_OWNER in the repository-root
.env.local. Development and preview builds append .dev and .preview to your bundle
identifier. Run eas init once under your Expo account to create the project ID, then use
the existing EAS iOS build commands below. EAS can perform the build remotely; a local
ios:* build still requires macOS and Xcode.
For temporary testing on your own iPhone, a borrowed Mac and a free Apple Account are enough. This mode removes capabilities that a Personal Team cannot sign: widgets and Live Activities, push notifications, App Groups, associated domains, and EAS updates. Apple expires free provisioning profiles after seven days, so the app must then be rebuilt and reinstalled.
On the Mac:
- Install Xcode, open it once, accept its license, and add your Apple Account under Xcode → Settings → Accounts.
- Enable Developer Mode on the iPhone and connect it to the Mac by USB.
- Set
T3CODE_MOBILE_IOS_BUNDLE_IDENTIFIER=com.example.t3codein the repository-root.env.local.T3CODE_MOBILE_IOS_TEAM_IDis optional; leave it unset to select your Personal Team interactively in Xcode. - From
apps/mobile, runvp run config:personalto confirm thatassociatedDomainsand theexpo-widgetsplugin are absent. - Run
vp run ios:personal. If Xcode requests a team, openios/T3Code.xcworkspace, select the main T3Code target, choose Signing & Capabilities → Team → your name (Personal Team), select your iPhone as the run destination, and press Run in Xcode. Do not run the clean prebuild command again after choosing the team, because it regeneratesios/.
The personal build uses a separate development bundle ID, so it remains separate from a future production build.
Start Metro for the dev client:
vp run dev:clientMetro keeps its transform cache between ordinary starts. If the cache itself is causing stale or invalid output, clear it for one development-client start:
vp run dev:client:resetRun that reset once after installing or changing the Uniwind dependency patch. Cached transforms can otherwise reference its previous pnpm package path. Ordinary Metro starts still keep the cache.
Component edits use Fast Refresh. See mobile development lifecycle before changing runtime ownership or refresh behavior.
Build and run the local iOS dev client:
vp run ios:devAfter changing a native dependency patch, rerun CocoaPods before rebuilding an existing iOS project. pnpm gives each patch hash a new package path; Pods can otherwise keep compiling the previous directory.
If your Xcode account only has a Personal Team, use a bundle identifier you control and opt into the reduced-capability local build. Personal Team builds omit the widget and share extensions, push entitlement, and native Sign in with Apple entitlement; builds without this opt-in are unchanged.
T3CODE_IOS_PERSONAL_TEAM=1 \
T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID=com.example.t3code.dev \
vp run ios:devBuild and install a self-contained Release app that does not need Metro:
vp run ios:releaseThe Personal Team equivalent also needs a unique bundle identifier:
T3CODE_IOS_PERSONAL_TEAM=1 \
T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID=com.example.t3code \
vp run ios:releaseBuild and run the local iOS preview app:
vp run ios:previewForce the review diff highlighter engine:
EXPO_PUBLIC_REVIEW_HIGHLIGHTER_ENGINE=javascript vp run ios:devjavascript is the default and recommended setting for the review diff screen. Set EXPO_PUBLIC_REVIEW_HIGHLIGHTER_ENGINE=native only when you explicitly want to test the native Shiki engine.
Inspect the resolved Expo config for a variant:
vp run config:dev
vp run config:previewRun static checks for mobile native code:
node ../../scripts/mobile-native-static-check.tsThe native lint task runs SwiftLint for Swift plus ktlint and detekt for Kotlin. Missing native tools are reported as warnings and skipped locally. CI installs the default toolset from apps/mobile/Brewfile before running the native checks.
Preview and production variants use Expo fingerprinting so OTA updates only reach binaries with matching native dependencies, config plugins, and patches. CI uses the preview profile to reuse an existing compatible build when possible, or start a new internal EAS build when native runtime inputs change. That profile builds the release configuration, so labelled PR builds behave like production for performance and memory; use preview:dev when you want a dev client on the preview channel that attaches to local Metro.
The development variant uses appVersion to avoid recalculating the native fingerprint for each Metro launch manifest. MOBILE_VERSION_POLICY can override either default. MOBILE_RUNTIME_VERSION_OVERRIDE pins an explicit runtime version when CI must ship pure-JS fixes to an already-installed binary (for example when the Free-plan build quota is exhausted). If you distribute a custom Release build with the development identity and publish OTA updates to it, set MOBILE_VERSION_POLICY=fingerprint for both its build and updates. Changing the runtime policy requires a native rebuild for OTA matching; an existing dev client can still load local Metro bundles.
For preview or production EAS environments, set T3CODE_CLERK_PUBLISHABLE_KEY,
T3CODE_CLERK_JWT_TEMPLATE, and T3CODE_RELAY_URL
as EAS environment variables. Expo config maps the canonical values into the mobile build.
Create a PR preview dev-client build manually:
vp run eas:ios:preview:devCreate a cloud dev-client build:
vp run eas:ios:devCreate a persistent preview build:
vp run eas:ios:previewAndroid equivalents:
vp run eas:android:dev
vp run eas:android:preview:dev
vp run eas:android:preview