1import { promptDeviceAsync } from './promptDevice'; 2import * as Log from '../../../log'; 3import { 4 AppleDeviceManager, 5 ensureSimulatorOpenAsync, 6} from '../../../start/platforms/ios/AppleDeviceManager'; 7import { assertSystemRequirementsAsync } from '../../../start/platforms/ios/assertSystemRequirements'; 8import { sortDefaultDeviceToBeginningAsync } from '../../../start/platforms/ios/promptAppleDevice'; 9import { OSType } from '../../../start/platforms/ios/simctl'; 10import * as SimControl from '../../../start/platforms/ios/simctl'; 11import { CommandError } from '../../../utils/errors'; 12import { profile } from '../../../utils/profile'; 13import { logDeviceArgument } from '../../hints'; 14import * as AppleDevice from '../appleDevice/AppleDevice'; 15 16type AnyDevice = SimControl.Device | AppleDevice.ConnectedDevice; 17 18/** Get a list of devices (called destinations) that are connected to the host machine. Filter by `osType` if defined. */ 19async function getDevicesAsync({ osType }: { osType?: OSType } = {}): Promise<AnyDevice[]> { 20 const connectedDevices = await AppleDevice.getConnectedDevicesAsync(); 21 22 const simulators = await sortDefaultDeviceToBeginningAsync( 23 await profile(SimControl.getDevicesAsync)(), 24 osType 25 ); 26 27 const devices = [...connectedDevices, ...simulators]; 28 29 // If osType is defined, then filter out ineligible simulators. 30 // Only do this inside of the device selection so users who pass the entire device udid can attempt to select any simulator (even if it's invalid). 31 return osType ? filterDevicesForOsType(devices, osType) : devices; 32} 33 34/** @returns a list of devices, filtered by the provided `osType`. */ 35function filterDevicesForOsType(devices: AnyDevice[], osType: OSType): AnyDevice[] { 36 return devices.filter((device) => !('osType' in device) || device.osType === osType); 37} 38 39/** Given a `device` argument from the CLI, parse and prompt our way to a usable device for building. */ 40export async function resolveDeviceAsync( 41 device?: string | boolean, 42 { osType }: { osType?: OSType } = {} 43): Promise<AnyDevice> { 44 await assertSystemRequirementsAsync(); 45 46 if (!device) { 47 /** Finds the first possible device and returns in a booted state. */ 48 const manager = await AppleDeviceManager.resolveAsync({ 49 device: { 50 osType, 51 }, 52 }); 53 Log.debug( 54 `Resolved default device (name: ${manager.device.name}, udid: ${manager.device.udid}, osType: ${osType})` 55 ); 56 return manager.device; 57 } 58 59 const devices: AnyDevice[] = await getDevicesAsync({ 60 osType, 61 }); 62 63 const resolved = 64 device === true 65 ? // `--device` (no props after) 66 await promptDeviceAsync(devices) 67 : // `--device <name|udid>` 68 findDeviceFromSearchValue(devices, device.toLowerCase()); 69 70 return ensureBootedAsync(resolved); 71} 72 73/** @returns `true` if the given device is a simulator. */ 74export function isSimulatorDevice(device: AnyDevice): boolean { 75 return ( 76 !('deviceType' in device) || 77 device.deviceType.startsWith('com.apple.CoreSimulator.SimDeviceType.') 78 ); 79} 80 81/** @returns device matching the `searchValue` against name or UDID. */ 82function findDeviceFromSearchValue(devices: AnyDevice[], searchValue: string): AnyDevice { 83 const device = devices.find( 84 (device) => 85 device.udid.toLowerCase() === searchValue || device.name.toLowerCase() === searchValue 86 ); 87 if (!device) { 88 throw new CommandError('BAD_ARGS', `No device UDID or name matching "${searchValue}"`); 89 } 90 return device; 91} 92 93/** Ensures the device is booted if it's a simulator. */ 94async function ensureBootedAsync(device: AnyDevice): Promise<AnyDevice> { 95 // --device with no props after 96 logDeviceArgument(device.udid); 97 if (isSimulatorDevice(device)) { 98 return ensureSimulatorOpenAsync({ udid: device.udid }); 99 } 100 return device; 101} 102