| ... | @@ -806,6 +806,47 @@ async function ensureIosSimulator(device: ShowcaseIosDevice): Promise<{ | ... | @@ -806,6 +806,47 @@ async function ensureIosSimulator(device: ShowcaseIosDevice): Promise<{ |
| 806 | }; | 806 | }; |
| 807 | } | 807 | } |
| 808 | | 808 | |
| | 809 | async function iosSimulatorDataPath(udid: string): Promise<string> { |
| | 810 | const parsed = JSON.parse(await commandOutput("xcrun", ["simctl", "list", "devices", "-j"])) as { |
| | 811 | readonly devices: Readonly< |
| | 812 | Record<string, ReadonlyArray<SimctlDevice & { readonly dataPath: string }>> |
| | 813 | >; |
| | 814 | }; |
| | 815 | const dataPath = Object.values(parsed.devices) |
| | 816 | .flat() |
| | 817 | .find((device) => device.udid === udid)?.dataPath; |
| | 818 | if (!dataPath) throw new Error(`Could not resolve the data path of iOS simulator ${udid}.`); |
| | 819 | return dataPath; |
| | 820 | } |
| | 821 | |
| | 822 | // generativeexperiencesd posts a "Ready for Apple Intelligence" follow-up |
| | 823 | // banner on an eligible device's first boot, and CoreFollowUp re-surfaces it |
| | 824 | // on every boot until the user dismisses it. Stamping the readiness marker |
| | 825 | // before boot makes the daemon skip the post, and dropping the CoreFollowUp |
| | 826 | // store clears a banner that a previous boot already queued. Runs while the |
| | 827 | // device is shut down so the files are read fresh on the next boot. |
| | 828 | async function suppressIosSystemFollowUps(udid: string): Promise<void> { |
| | 829 | const dataPath = await iosSimulatorDataPath(udid); |
| | 830 | const preferences = NodePath.join(dataPath, "Library/Preferences"); |
| | 831 | await NodeFSP.mkdir(preferences, { recursive: true }); |
| | 832 | await NodeFSP.writeFile( |
| | 833 | NodePath.join(preferences, "com.apple.generativeexperiences.corefollowup.plist"), |
| | 834 | `<?xml version="1.0" encoding="UTF-8"?> |
| | 835 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| | 836 | <plist version="1.0"> |
| | 837 | <dict> |
| | 838 | \t<key>DateOfLastAppleIntelligenceReadinessCFU</key> |
| | 839 | \t<date>2020-01-01T00:00:00Z</date> |
| | 840 | </dict> |
| | 841 | </plist> |
| | 842 | `, |
| | 843 | ); |
| | 844 | await NodeFSP.rm(NodePath.join(dataPath, "Library/CoreFollowUp"), { |
| | 845 | recursive: true, |
| | 846 | force: true, |
| | 847 | }); |
| | 848 | } |
| | 849 | |
| 809 | async function normalizeIosSimulator(appearance: ShowcaseAppearance, udid: string): Promise<void> { | 850 | async function normalizeIosSimulator(appearance: ShowcaseAppearance, udid: string): Promise<void> { |
| 810 | await runCommand("xcrun", ["simctl", "ui", udid, "appearance", appearance]); | 851 | await runCommand("xcrun", ["simctl", "ui", udid, "appearance", appearance]); |
| 811 | await runCommand("xcrun", [ | 852 | await runCommand("xcrun", [ |
| ... | @@ -922,6 +963,7 @@ async function captureIos( | ... | @@ -922,6 +963,7 @@ async function captureIos( |
| 922 | // confirmations, keyboards) without erasing the developer's simulator. | 963 | // confirmations, keyboards) without erasing the developer's simulator. |
| 923 | await runCommand("xcrun", ["simctl", "shutdown", simulator.udid]); | 964 | await runCommand("xcrun", ["simctl", "shutdown", simulator.udid]); |
| 924 | } | 965 | } |
| | 966 | await suppressIosSystemFollowUps(simulator.udid); |
| 925 | await runCommand("xcrun", ["simctl", "boot", simulator.udid]); | 967 | await runCommand("xcrun", ["simctl", "boot", simulator.udid]); |
| 926 | await runCommand("xcrun", ["simctl", "bootstatus", simulator.udid, "-b"]); | 968 | await runCommand("xcrun", ["simctl", "bootstatus", simulator.udid, "-b"]); |
| 927 | if (capture.device.orientation === "landscape") { | 969 | if (capture.device.orientation === "landscape") { |