Closes #193, #194, #195 and #196.
Where the four issues stood
All four were filed on 14 September, and each was fixed in the generator the same day, a few hours later:
| issue | reported | fixed by |
|---|---|---|
| #193 | phone target fails: CraftWatchSession does not conform to WCSessionDelegate | 6bd84f4 moves the Watch source into a watchOS-only extension, with the iOS-only delegate methods behind #if os(iOS) (5d03387) |
| #194 | simctl install refuses: no WKWatchKitApp or WKApplication key | WatchApp.Info.plist.template sets WKWatchKitApp |
| #195 | simctl install refuses: "not a WatchKit 2 app" | 6bd84f4 generates application.watchapp2 plus a watchkit2-extension embedded in it |
| #196 | aps-environment is always development | 611be12: $(CRAFT_APNS_ENVIRONMENT), set to development for Debug and production for Release |
None of the fixes was checked where it failed. test:templates built the Watch extension and Watch app targets on their own, which says nothing about the bundle they end up in. Its comment said the embedded app could not be built "without a watchOS runtime", and GitHub's macOS image ships one. index.test.ts checked the APNs change as two strings in project.yml, which proves what was written, not what Xcode signs.
What the gate does now
packages/ios/scripts/compile-templates.ts, run by ci.yml's ios-native-templates job:
The Watch companion (#193–#195). The script builds WatchProbe through its scheme for iOS Simulator, with the Watch app embedded, and installs it on a booted iOS simulator.
xcodebuildbuilds every target in the graph with its own SDK. That covers #193: the phone target compiles.- Before installing,
watchCompanionProblemschecks the built bundle. It requiresWatch/<App>Watch.app,WKWatchKitApp(orWKApplication) true, andPlugIns/<App>WatchExtension.appexdeclaringcom.apple.watchkit. Each failure names its issue. The helper has unit tests inwatch-bundle.test.ts. simctl installthen has the last word, and the app is uninstalled afterwards.
It needs a watchOS simulator runtime matching the selected Xcode. macos-latest ships watchOS 11.5, 26.1 and 26.2. Without one, Xcode's own error ("watchOS N must be installed") fails the step rather than skipping it.
The APNs environment (#196). A push-only fixture is built for the simulator in Debug and Release with ad-hoc signing (CODE_SIGN_IDENTITY=-). Ad-hoc signing needs no team and still runs entitlement processing. The script reads <App>.app-Simulated.xcent, the entitlements Xcode actually produced, and requires development for Debug and production for Release. The build-setting substitution is the one device signing performs; only the signature differs.
Failing without the fixes
Each fix was reverted in the generator in turn, and the same script run:
# WKWatchKitApp removed from WatchApp.Info.plist.template
error: WatchProbe.app: Watch/WatchProbeWatch.app sets neither WKWatchKitApp nor WKApplication to true, so iOS will not install it (#194)
# the Watch app's dependency on its extension removed
error: WatchProbe.app: Watch/WatchProbeWatch.app has no PlugIns/WatchProbeWatchExtension.appex, so it is not a WatchKit 2 app (#195)
# aps-environment hard-coded back to development
ok: Debug signs aps-environment=development
error: Release signs aps-environment=development, expected production#193 has no separate mutation. Its error cannot occur in the current structure, because the Watch source is compiled only for watchOS. What guards it is the embedded build compiling the phone target at all. The reporter's xcodebuild -target … -sdk iphonesimulator form also no longer applies: forcing one SDK onto every target cannot resolve the watchOS product types. The scheme build is the command Xcode itself runs.
Choices
- WatchKit 2, not a single-target Watch app. #195 asked for the extension structure and
6bd84f4generated it, so this PR tests what the generator emits rather than re-deciding it. The checker acceptsWKApplicationtoo, so a later move to single-target Watch apps does not have to fight the gate. - The APNs environment is derived from the build configuration, with no
pushEnvironmentoption. That is how Xcode's own push capability behaves, and it cannot drift from the configuration being archived.
Verification
- Locally, with the watchOS 26.5 simulator runtime installed: the Watch and push parts of
test:templatespass, and the three mutations above fail. bun testinpackages/ios(34, including 6 new),bun run test,bun run typecheck,bun run verify:packagesand Pickier.- The full template run on
macos-latestis this PR's CI.