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