WristCode watch app
SwiftUI, watch only, Swift 6 with strict concurrency, and no dependencies. It talks to the gateway
over HTTPS + JSON as described in docs/api.md; WristCode/API/Models.swift
mirrors gateway/src/contract.ts.
Build and test
cd watch
xcodegen generate # the .xcodeproj is generated and gitignored
xcodebuild -scheme WristCode -destination 'platform=watchOS Simulator,name=Apple Watch Series 12 (46mm)' build
xcodebuild -scheme WristCode -destination 'platform=watchOS Simulator,name=Apple Watch Series 12 (46mm)' test
TestFlight
The App Store only takes a watch-only app inside an empty iOS stub, so project.yml has two app
targets, the same pair Xcode's "Watch-only App" template makes:
| Target | Bundle ID | Job |
|---|---|---|
WristCodeContainer |
org.gbrown.wristcode |
iOS stub that owns the App Store Connect record and embeds the watch app |
WristCode |
org.gbrown.wristcode.watchkitapp |
The watch app, and the APNs topic |
WristCodeWidget |
org.gbrown.wristcode.watchkitapp.widget |
The complication, embedded in the watch app |
scripts/testflight.sh # archive, export and upload to App Store Connect
scripts/testflight.sh --no-upload # archive and export an .ipa to build/export-<n> only
The build number defaults to a YYYYMMDDHHMM timestamp (override it with BUILD_NUMBER). Signing is
automatic through the App Store Connect API key in ASC_KEY_PATH, ASC_KEY_ID and ASC_ISSUER_ID,
which default to the key in ~/.appstoreconnect/private_keys. The entitlements file says
aps-environment: development; the App Store export re-signs it as production. Release builds
register their token with the gateway as production, Debug builds as sandbox.
Demo mode
DemoGateway serves fixture data shaped like the docs/api.md examples, and it reacts to writes:
Allow moves a thread to working, a reply restarts a finished thread, and Settle hides a thread from
the feed. One card carries a subagent's request, so its Allow goes to the subagent's thread. Recent
holds 14 threads, so the feed shows its first 10 and a "4 more" row. The working cards show
activity lines, the Containerfile thread has two subagents, and "Fix hyprlock crash" has two older
pages behind Load earlier. The SwiftUI previews use it,
the pairing screen has a "Try the demo" button, and launch arguments open a screen directly:
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo # feed
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo -thread littleman/t-next # thread
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo -screen new # new thread
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo -screen recent # full Recent list
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo -screen status # status overview
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo -host laptop # one environment
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo -screen notifications # notifications
xcrun simctl launch <udid> org.gbrown.wristcode.watchkitapp -demo -screen threads # All threads
screenshots/ holds the screens captured this way. The complication, the multi-select question, the
status screens, the subagent card and thread, Recent with Settle all and the "N more" row, the full
Recent list, a finished thread with Settle, a running thread with Ask before acting, New thread
with its model list, the working cards' activity lines, All threads' hosts and a project's threads,
a thread's subagents, Load earlier with markdown, the changes line and Changed list, search and
its empty state, the complication's two modes, the Smart Stack widget, and a question
notification are rendered by SnapshotTests instead, which only run when given a directory:
TEST_RUNNER_WC_SCREENSHOT_DIR=$PWD/screenshots xcodebuild -scheme WristCode \
-destination 'platform=watchOS Simulator,name=Apple Watch Series 12 (46mm)' test \
-only-testing:WristCodeTests/SnapshotTests
In demo mode the complication shows the demo counts too.
Subagents and settling
A thread an agent started for another thread has no card of its own. When it waits on you, the
gateway puts its request on the parent's card and names it in via. The card says "Subagent