Skip to content

Latest commit

 

History

History

ios

Folders and files

README.md

xMatrix iOS

This is a native iOS shell for the existing xMatrix web app. Electron cannot run on iOS, so this target reuses the same /app web surface through WKWebView and injects a compatible window.xmatrixDesktop bridge.

Open in Xcode

open apps/ios/xMatrix.xcodeproj

Select the xMatrix scheme, choose an iPhone simulator or device, then run.

Web URL

The app loads the Web named by its XMatrixWebURL, which is the XMATRIX_WEB_URL build setting (https://xmatrix.sh/app by default). It trusts only that Web's exact https host with in-place navigation and the bridge; the stable Web is also trusted under www.xmatrix.sh. scripts/ios-testflight.sh sets the build setting from the release version: a beta X.Y.Z-beta.N loads https://beta.xmatrix.sh/app, a stable X.Y.Z loads https://xmatrix.sh/app. The App Store version drops -beta.N, so the archive is the only place a build learns its channel.

For local development, set XMATRIX_WEB_URL to a reachable URL, either under the xMatrix target's User-Defined build settings or on the command line:

xcodebuild -project apps/ios/xMatrix.xcodeproj -scheme xMatrix XMATRIX_WEB_URL=http://localhost:3001/app build

When running on a physical device, use the Mac LAN IP instead of localhost.

App Store continuous delivery

Production Release Intent with release_components=ios runs the existing immutable release train, simulator gate, signing, and TestFlight upload. A beta stops at TestFlight; a stable continues with App Store version metadata and review submission. Both use the release's X.Y.Z as the store version and the run ID as the build number. The app icon is bundled by Xcode; selecting the exact uploaded build updates the store icon as part of that version.

Public listing text lives in app-store/metadata.json. Contact information, review credentials, and evidence remain in App Store Connect; never commit them here. See the App Store CD runbook for first-time setup, deferred review states, private notes, and recovery.

TestFlight

The release helper archives, exports, and uploads the app:

IOS_DEVELOPMENT_TEAM=YOUR_APPLE_TEAM_ID \
IOS_BUILD_NUMBER=2026050601 \
ASC_API_KEY_ID=YOUR_APP_STORE_CONNECT_KEY_ID \
ASC_API_ISSUER_ID=YOUR_APP_STORE_CONNECT_ISSUER_ID \
ASC_P8_PATH=/path/to/AuthKey_YOUR_KEY_ID.p8 \
PNPM ios:testflight

The TestFlight marketing version is the root version.json version. IOS_MARKETING_VERSION may only be set to that same value; mismatches fail before archiving.

Set SKIP_TESTFLIGHT_UPLOAD=1 to stop after exporting the IPA.

Bridge compatibility

The injected bridge mirrors the Electron preload API used by apps/web/src/lib/desktop/bridge.ts:

  • getContext, setBadge, notify, and openExternal map to native iOS APIs.
  • pushDevice asks for notification permission and returns the device's APNs token, which the web app registers with the Hub; the app carries the aps-environment entitlement for it.
  • checkCliInstalled always returns { "installed": false }.
  • Desktop auto-update methods return a disabled status because iOS updates are handled by the App Store or TestFlight.
  • xmatrix://channel/:id and xmatrix://login?... deep links are mapped to the same web routes as the Electron app.
  • <a download> links (including the blob: URLs attachment downloads use) and Content-Disposition: attachment responses become native WKDownloads: MP4/MOV videos and images are saved straight to Photos, everything else is offered to Files.

Native mobile tab dock

On iPhone the bottom tab dock is rendered by a native UIKit tab bar instead of the web surface. The dock has Pages, Channels, Agents, and More, in the web dock's order.

  • The app targets iOS 26 and uses the system UITabBar without a custom appearance. It is hosted by a transparent controller with no content child, so the native dock never covers the WKWebView. iOS supplies the native selection treatment and accessibility adaptations; the app does not simulate those with gradients, materials, overlays, or shaders.
  • The dock floats on the screen bottom, not on the home-indicator inset. Its side and bottom gap is the display corner radius minus the capsule's own corner radius (never under 12pt, and never so wide that the capsule is under 240pt), so the capsule shares a center with the device corners. The web layer receives that gap and the capsule's height above the home indicator.
  • The app is one WKWebView. A tab tap calls onMobileTabChange listeners in that page, which switch to the tab's pane; the web shell keeps one mounted pane per dock tab, so switching tabs never loads, boots, or rebuilds a screen. Deep links load in the same page.
  • setMobileTabState({ visible, activeView }) reports the page's current view and whether the dock shows; the native bar highlights the tab that owns the view (Pages, Channels and Agents own themselves, every other view is in More). The web layer also sends spaceId and userId, which only older app builds that kept a page per tab use.
  • The web-rendered MobileTabDock is hidden when the iOS shell is detected, so only the native capsule dock is visible.

Native startup regression tests

Run xcodebuild -project apps/ios/xMatrix.xcodeproj -scheme xMatrix -destination 'platform=iOS Simulator,name=iPhone 17 Pro' test from the repository root (or select another available iOS 26 simulator).

PR CI and the production release gate share .github/actions/ios-simulator-test: they select Xcode and an installed iOS 26 iPhone by UDID. The release gate tests the exact stamped release revision and fails if no suitable simulator is available.

The xMatrixTests target exercises the production coordinator with real WKWebView instances whose top-level load calls are recorded instead of sent to the network. It verifies single-page startup, tab taps that switch panes without loading, deep links in the same page, foreground resume, and bridge state decoding. These are lifecycle tests, not end-to-end network latency benchmarks.