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