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