Watch: TestFlight archive, iOS stub container, testflight.sh

The App Store only accepts a watch-only app inside an empty iOS stub
(watchapp2-container), the pair Xcode's Watch-only App template makes.
The stub takes org.gbrown.wristcode; the watch app, and so the APNs topic,
becomes org.gbrown.wristcode.watchkitapp.

Adds ITSAppUsesNonExemptEncryption = NO and scripts/testflight.sh, which
archives with API key automatic signing and a timestamp build number,
then exports and uploads (or exports only with --no-upload).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
gibandClaude Opus 5.5 committed 2026-11-01 10:08:55 -05:00
1 parent 16bae4410d
commit f772f4e4c4
4 files changed
+141 -4

No files matched your search

+24 -3
View File
@@ -13,6 +13,27 @@ xcodebuild -scheme WristCode -destination 'platform=watchOS Simulator,name=Apple
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 |
```sh
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:
@@ -20,9 +41,9 @@ Allow moves a thread to working, a reply restarts a finished thread. The SwiftUI
the pairing screen has a "Try the demo" button, and launch arguments open a screen directly:
```sh
xcrun simctl launch <udid> org.gbrown.wristcode -demo # feed
xcrun simctl launch <udid> org.gbrown.wristcode -demo -thread littleman/t-next # thread
xcrun simctl launch <udid> org.gbrown.wristcode -demo -screen new # new thread
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
```
`screenshots/` holds the screens captured this way.