ReviewOS

also looking at this

craft-native/craft

test(ios): install the Watch-enabled app and read the APNs environment it signs

#218
Merged glennmichael123 wants to merge fix/ios-apns-environment-resolved into main
6 files +248 -12
packages/ios/scripts/watch-bundle.tsadded+55-0
Changes to packages/ios/scripts/watch-bundle.ts
@@ -0,0 +1,55 @@
1import { existsSync } from 'node:fs'
2import { join } from 'node:path'
3
4/** A plist as JSON, or null when it cannot be read. */
5export type PlistReader = (path: string) => Record<string, unknown> | null
6
7/**
8 * `plutil`, which reads the binary plists Xcode writes into a built bundle.
9 * macOS only, like the builds whose output it reads.
10 */
11export const readPlistWithPlutil: PlistReader = (path) => {
12 const converted = Bun.spawnSync(['plutil', '-convert', 'json', '-o', '-', path])
13 if (converted.exitCode !== 0) return null
14 try {
15 return JSON.parse(converted.stdout.toString()) as Record<string, unknown>
16 }
17 catch {
18 return null
19 }
20}
21
22/**
23 * Why a built iOS app's embedded Watch companion would not install, one line
24 * per reason.
25 *
26 * Each line is a message `simctl install` gave for a generated app (#194,
27 * #195), checked in the bundle first so a failure names the missing piece
28 * rather than only quoting simctl. The install itself still runs afterwards;
29 * this does not replace it.
30 */
31export function watchCompanionProblems(app: string, name: string, readPlist: PlistReader = readPlistWithPlutil): string[] {
32 const watch = join(app, 'Watch', `${name}Watch.app`)
33 if (!existsSync(watch))
34 return [`${name}.app embeds no Watch/${name}Watch.app`]
35
36 const problems: string[] = []
37 const watchInfo = readPlist(join(watch, 'Info.plist'))
38 if (!watchInfo)
39 problems.push(`Watch/${name}Watch.app has no readable Info.plist`)
40 else if (watchInfo.WKWatchKitApp !== true && watchInfo.WKApplication !== true)
41 problems.push(`Watch/${name}Watch.app sets neither WKWatchKitApp nor WKApplication to true, so iOS will not install it (#194)`)
42
43 const extension = join(watch, 'PlugIns', `${name}WatchExtension.appex`)
44 if (!existsSync(extension)) {
45 problems.push(`Watch/${name}Watch.app has no PlugIns/${name}WatchExtension.appex, so it is not a WatchKit 2 app (#195)`)
46 return problems
47 }
48
49 const extensionInfo = readPlist(join(extension, 'Info.plist'))
50 const point = (extensionInfo?.NSExtension as Record<string, unknown> | undefined)?.NSExtensionPointIdentifier
51 if (point !== 'com.apple.watchkit')
52 problems.push(`${name}WatchExtension.appex declares extension point ${JSON.stringify(point)}, not com.apple.watchkit`)
53
54 return problems
55}