1import { execAsync } from '@expo/osascript';
2import spawnAsync from '@expo/spawn-async';
3
4import * as Log from '../../../log';
5import { Prerequisite, PrerequisiteCommandError } from '../Prerequisite';
6
7async function getSimulatorAppIdAsync(): Promise<string | null> {
8  try {
9    return (await execAsync('id of app "Simulator"')).trim();
10  } catch {
11    // This error may occur in CI where the users intends to install just the simulators but no Xcode.
12  }
13  return null;
14}
15
16export class SimulatorAppPrerequisite extends Prerequisite {
17  static instance = new SimulatorAppPrerequisite();
18
19  async assertImplementation(): Promise<void> {
20    const result = await getSimulatorAppIdAsync();
21    if (!result) {
22      // This error may occur in CI where the users intends to install just the simulators but no Xcode.
23      throw new PrerequisiteCommandError(
24        'SIMULATOR_APP',
25        "Can't determine id of Simulator app; the Simulator is most likely not installed on this machine. Run `sudo xcode-select -s /Applications/Xcode.app`"
26      );
27    }
28    if (
29      result !== 'com.apple.iphonesimulator' &&
30      result !== 'com.apple.CoreSimulator.SimulatorTrampoline'
31    ) {
32      throw new PrerequisiteCommandError(
33        'SIMULATOR_APP',
34        "Simulator is installed but is identified as '" + result + "'; don't know what that is."
35      );
36    }
37
38    try {
39      // make sure we can run simctl
40      await spawnAsync('xcrun', ['simctl', 'help']);
41    } catch (error: any) {
42      Log.warn(`Unable to run simctl:\n${error.toString()}`);
43      throw new PrerequisiteCommandError(
44        'SIMCTL',
45        'xcrun is not configured correctly. Ensure `sudo xcode-select --reset` works before running this command again.'
46      );
47    }
48  }
49}
50