From 3565c87ddfa2f57a8223fecfb4d39508b8eefa1e Mon Sep 17 00:00:00 2001 From: Erik Thorelli Date: Fri, 12 Aug 2022 09:54:49 -0700 Subject: [PATCH 1/7] get basic listing of device names working --- .../cli-platform-ios/src/commands/index.ts | 3 +- .../src/commands/listIOS/index.ts | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 packages/cli-platform-ios/src/commands/listIOS/index.ts diff --git a/packages/cli-platform-ios/src/commands/index.ts b/packages/cli-platform-ios/src/commands/index.ts index 2dc3c640e..ee255dbe1 100644 --- a/packages/cli-platform-ios/src/commands/index.ts +++ b/packages/cli-platform-ios/src/commands/index.ts @@ -1,4 +1,5 @@ +import listIOS from './listIOS'; import logIOS from './logIOS'; import runIOS from './runIOS'; -export default [logIOS, runIOS]; +export default [listIOS, logIOS, runIOS]; diff --git a/packages/cli-platform-ios/src/commands/listIOS/index.ts b/packages/cli-platform-ios/src/commands/listIOS/index.ts new file mode 100644 index 000000000..a3edaa703 --- /dev/null +++ b/packages/cli-platform-ios/src/commands/listIOS/index.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {execFileSync} from 'child_process'; +import {logger} from '@react-native-community/cli-tools'; +import {Device} from '../../types'; + +function findAvailableDevices(devices: {[index: string]: Array}) { + // TODO -- It would be cool to separate out the Apple Watch, iOS, iPad, TV, and others... + let availableDevices = []; + for (const key of Object.keys(devices)) { + for (const device of devices[key]) { + if ( + device.availability === '(available)' || + device.isAvailable === true + ) { + availableDevices.push(device.name); + } + } + } + return availableDevices; +} + +async function listIOS() { + const rawDevices = execFileSync( + 'xcrun', + ['simctl', 'list', 'devices', '--json'], + {encoding: 'utf8'}, + ); + + const {devices} = JSON.parse(rawDevices) as { + devices: {[index: string]: Array}; + }; + + const availableDevices = findAvailableDevices(devices); + if (availableDevices === null) { + logger.error('No available iOS devices found'); + return; + } + logger.info(JSON.stringify(availableDevices)); +} + +export default { + name: 'list-ios', + description: 'lists available iOS devices', + func: listIOS, +}; From 46e9e27e2e6a7816e88284cdc3b6247fd7389626 Mon Sep 17 00:00:00 2001 From: Erik Thorelli Date: Fri, 12 Aug 2022 10:38:56 -0700 Subject: [PATCH 2/7] get a nice selector prompt going --- .../src/commands/listIOS/index.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/cli-platform-ios/src/commands/listIOS/index.ts b/packages/cli-platform-ios/src/commands/listIOS/index.ts index a3edaa703..3f56b0c4e 100644 --- a/packages/cli-platform-ios/src/commands/listIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/listIOS/index.ts @@ -7,6 +7,8 @@ */ import {execFileSync} from 'child_process'; +import prompts from 'prompts'; +import chalk from 'chalk'; import {logger} from '@react-native-community/cli-tools'; import {Device} from '../../types'; @@ -42,7 +44,23 @@ async function listIOS() { logger.error('No available iOS devices found'); return; } - logger.info(JSON.stringify(availableDevices)); + + async function promptForDeviceSelection(): Promise { + const {device} = await prompts({ + type: 'select', + name: 'device', + message: 'Select the device you want to use', + choices: availableDevices.map((deviceName) => ({ + title: `${chalk.dim(`(${deviceName})`)}`, + value: deviceName, + // selected: DEFAULT_GROUPS.includes(cmd), + })), + min: 1, + }); + return device; + } + + promptForDeviceSelection(); } export default { From 968ea146226c2d6b794af8b87364b8321ba9a3e4 Mon Sep 17 00:00:00 2001 From: Erik Thorelli Date: Fri, 12 Aug 2022 10:53:22 -0700 Subject: [PATCH 3/7] tidy up the selector --- packages/cli-platform-ios/src/commands/listIOS/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/listIOS/index.ts b/packages/cli-platform-ios/src/commands/listIOS/index.ts index 3f56b0c4e..e2ac2885d 100644 --- a/packages/cli-platform-ios/src/commands/listIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/listIOS/index.ts @@ -51,9 +51,8 @@ async function listIOS() { name: 'device', message: 'Select the device you want to use', choices: availableDevices.map((deviceName) => ({ - title: `${chalk.dim(`(${deviceName})`)}`, + title: `${chalk.bold(`(${deviceName})`)}`, value: deviceName, - // selected: DEFAULT_GROUPS.includes(cmd), })), min: 1, }); From 5e6ce9f480413026e3a2eec0611f3fd58faa623b Mon Sep 17 00:00:00 2001 From: Adam Trzcinski Date: Fri, 20 Jan 2023 15:52:10 +0100 Subject: [PATCH 4/7] connect list-devices to run-ios command --- .../cli-platform-ios/src/commands/index.ts | 3 +- .../src/commands/listIOS/index.ts | 69 ------------------- .../src/commands/runIOS/index.ts | 52 ++++++++------ .../src/commands/runIOS/listIOSDevices.ts | 46 +++++++++++++ 4 files changed, 79 insertions(+), 91 deletions(-) delete mode 100644 packages/cli-platform-ios/src/commands/listIOS/index.ts create mode 100644 packages/cli-platform-ios/src/commands/runIOS/listIOSDevices.ts diff --git a/packages/cli-platform-ios/src/commands/index.ts b/packages/cli-platform-ios/src/commands/index.ts index ee255dbe1..2dc3c640e 100644 --- a/packages/cli-platform-ios/src/commands/index.ts +++ b/packages/cli-platform-ios/src/commands/index.ts @@ -1,5 +1,4 @@ -import listIOS from './listIOS'; import logIOS from './logIOS'; import runIOS from './runIOS'; -export default [listIOS, logIOS, runIOS]; +export default [logIOS, runIOS]; diff --git a/packages/cli-platform-ios/src/commands/listIOS/index.ts b/packages/cli-platform-ios/src/commands/listIOS/index.ts deleted file mode 100644 index e2ac2885d..000000000 --- a/packages/cli-platform-ios/src/commands/listIOS/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -import {execFileSync} from 'child_process'; -import prompts from 'prompts'; -import chalk from 'chalk'; -import {logger} from '@react-native-community/cli-tools'; -import {Device} from '../../types'; - -function findAvailableDevices(devices: {[index: string]: Array}) { - // TODO -- It would be cool to separate out the Apple Watch, iOS, iPad, TV, and others... - let availableDevices = []; - for (const key of Object.keys(devices)) { - for (const device of devices[key]) { - if ( - device.availability === '(available)' || - device.isAvailable === true - ) { - availableDevices.push(device.name); - } - } - } - return availableDevices; -} - -async function listIOS() { - const rawDevices = execFileSync( - 'xcrun', - ['simctl', 'list', 'devices', '--json'], - {encoding: 'utf8'}, - ); - - const {devices} = JSON.parse(rawDevices) as { - devices: {[index: string]: Array}; - }; - - const availableDevices = findAvailableDevices(devices); - if (availableDevices === null) { - logger.error('No available iOS devices found'); - return; - } - - async function promptForDeviceSelection(): Promise { - const {device} = await prompts({ - type: 'select', - name: 'device', - message: 'Select the device you want to use', - choices: availableDevices.map((deviceName) => ({ - title: `${chalk.bold(`(${deviceName})`)}`, - value: deviceName, - })), - min: 1, - }); - return device; - } - - promptForDeviceSelection(); -} - -export default { - name: 'list-ios', - description: 'lists available iOS devices', - func: listIOS, -}; diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 5bdf81429..993d108e4 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -14,8 +14,6 @@ import child_process, { import path from 'path'; import chalk from 'chalk'; import {Config, IOSProjectInfo} from '@react-native-community/cli-types'; -import parseIOSDevicesList from './parseIOSDevicesList'; -import parseXctraceIOSDevicesList from './parseXctraceIOSDevicesList'; import findMatchingSimulator from './findMatchingSimulator'; import { logger, @@ -24,7 +22,7 @@ import { } from '@react-native-community/cli-tools'; import {Device} from '../../types'; import ora from 'ora'; -import execa from 'execa'; +import listIOSDevices, {promptForDeviceSelection} from './listIOSDevices'; type FlagsT = { simulator?: string; @@ -37,9 +35,10 @@ type FlagsT = { verbose: boolean; port: number; terminal: string | undefined; + listDevices?: boolean; }; -function runIOS(_: Array, ctx: Config, args: FlagsT) { +async function runIOS(_: Array, ctx: Config, args: FlagsT) { if (!ctx.project.ios) { throw new CLIError( 'iOS project folder not found. Are you sure this is a React Native project?', @@ -68,6 +67,28 @@ function runIOS(_: Array, ctx: Config, args: FlagsT) { } "${chalk.bold(xcodeProject.name)}"`, ); + if (args.listDevices) { + if (args.device || args.udid) { + logger.warn( + `Both ${ + args.device ? 'device' : 'udid' + } and "list-devices" parameters were passed to "run" command. We will list available devices and let you choose from one.`, + ); + } + const availableDevices = await listIOSDevices(); + const selectedDevice = await promptForDeviceSelection(availableDevices); + if (!selectedDevice) { + throw new CLIError( + 'Failed to select device, please try to run app without "list-devices" command.', + ); + } + if (selectedDevice.type === 'simulator') { + return runOnSimulator(xcodeProject, scheme, args); + } else { + return runOnDevice(selectedDevice, scheme, xcodeProject, args); + } + } + // No need to load all available devices if (!args.device && !args.udid) { return runOnSimulator(xcodeProject, scheme, args); @@ -79,21 +100,7 @@ function runIOS(_: Array, ctx: Config, args: FlagsT) { ); } - let devices; - try { - const out = execa.sync('xcrun', ['xctrace', 'list', 'devices']); - devices = parseXctraceIOSDevicesList( - // Xcode 12.5 introduced a change to output the list to stdout instead of stderr - out.stderr === '' ? out.stdout : out.stderr, - ); - } catch (e) { - logger.warn( - 'Support for Xcode 11 and older is deprecated. Please upgrade to Xcode 12.', - ); - devices = parseIOSDevicesList( - execa.sync('xcrun', ['instruments', '-s']).stdout, - ); - } + const devices = await listIOSDevices(); if (args.udid) { const device = devices.find((d) => d.udid === args.udid); @@ -625,7 +632,12 @@ export default { name: '--terminal ', description: 'Launches the Metro Bundler in a new window using the specified terminal path.', - default: getDefaultUserTerminal, + default: getDefaultUserTerminal(), + }, + { + name: '--list-devices', + description: + 'List all available iOS devices and simulators and let you choose one to run the app. ', }, ], }; diff --git a/packages/cli-platform-ios/src/commands/runIOS/listIOSDevices.ts b/packages/cli-platform-ios/src/commands/runIOS/listIOSDevices.ts new file mode 100644 index 000000000..a91bbf4d9 --- /dev/null +++ b/packages/cli-platform-ios/src/commands/runIOS/listIOSDevices.ts @@ -0,0 +1,46 @@ +import {Device} from '../../types'; +import parseIOSDevicesList from './parseIOSDevicesList'; +import parseXctraceIOSDevicesList from './parseXctraceIOSDevicesList'; +import execa from 'execa'; +import {logger} from '@react-native-community/cli-tools'; +import prompts from 'prompts'; +import chalk from 'chalk'; + +export async function promptForDeviceSelection( + availableDevices: Device[], +): Promise { + const {device} = await prompts({ + type: 'select', + name: 'device', + message: 'Select the device you want to use', + choices: availableDevices + .filter((d) => d.type === 'device' || d.type === 'simulator') + .map((d) => ({ + title: `${chalk.bold(d.name)}`, + value: d, + })), + min: 1, + }); + return device; +} + +async function listIOSDevices(): Promise { + let devices; + try { + const out = execa.sync('xcrun', ['xctrace', 'list', 'devices']); + devices = parseXctraceIOSDevicesList( + // Xcode 12.5 introduced a change to output the list to stdout instead of stderr + out.stderr === '' ? out.stdout : out.stderr, + ); + } catch (e) { + logger.warn( + 'Support for Xcode 11 and older is deprecated. Please upgrade to Xcode 12.', + ); + devices = parseIOSDevicesList( + execa.sync('xcrun', ['instruments', '-s']).stdout, + ); + } + return devices; +} + +export default listIOSDevices; From 117844675fdff7a563391e5120a7623c60241e27 Mon Sep 17 00:00:00 2001 From: Adam Trzcinski Date: Fri, 20 Jan 2023 16:01:36 +0100 Subject: [PATCH 5/7] fix TS error --- packages/cli-platform-ios/src/commands/runIOS/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 0b0e55454..9705a1e7b 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -104,6 +104,8 @@ async function runIOS(_: Array, ctx: Config, args: FlagsT) { } } + const devices = await listIOSDevices(); + // No need to load all available devices if (!args.device && !args.udid) { const bootedDevices = devices.filter(({type}) => type === 'device'); @@ -139,8 +141,6 @@ async function runIOS(_: Array, ctx: Config, args: FlagsT) { ); } - const devices = await listIOSDevices(); - if (args.udid) { const device = devices.find((d) => d.udid === args.udid); if (!device) { From a8d3e97536eb92c77dd8482e13a736a7817835a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Trzci=C5=84ski?= Date: Fri, 20 Jan 2023 18:07:01 +0100 Subject: [PATCH 6/7] Update packages/cli-platform-ios/src/commands/runIOS/index.ts Co-authored-by: Szymon Rybczak --- packages/cli-platform-ios/src/commands/runIOS/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 9705a1e7b..2d4dcc592 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -98,7 +98,7 @@ async function runIOS(_: Array, ctx: Config, args: FlagsT) { ); } if (selectedDevice.type === 'simulator') { - return runOnSimulator(xcodeProject, scheme, args); + return runOnSimulator(xcodeProject, scheme, args, selectedDevice); } else { return runOnDevice(selectedDevice, scheme, xcodeProject, args); } From 2e97b1966138233e516eadf65c3145e214d5896a Mon Sep 17 00:00:00 2001 From: Adam Trzcinski Date: Fri, 20 Jan 2023 18:09:27 +0100 Subject: [PATCH 7/7] code review fixes --- docs/commands.md | 6 ++++++ .../src/commands/runIOS/index.ts | 19 ------------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 2b6ae31d5..9186f2782 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -494,6 +494,12 @@ Location for iOS build artifacts. Corresponds to Xcode's `-derivedDataPath`. Installs passed binary instead of building a fresh one. +#### `--list-devices` + +> default: false + +List all available iOS devices and simulators and let you choose one to run the app. + ### `start` Usage: diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 9705a1e7b..35fe1836f 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -164,25 +164,6 @@ async function runIOS(_: Array, ctx: Config, args: FlagsT) { } } -// const getDevices = () => { -// let devices; -// try { -// const out = execa.sync('xcrun', ['xctrace', 'list', 'devices']); -// devices = parseXctraceIOSDevicesList( -// // Xcode 12.5 introduced a change to output the list to stdout instead of stderr -// out.stderr === '' ? out.stdout : out.stderr, -// ); -// } catch (e) { -// logger.warn( -// 'Support for Xcode 11 and older is deprecated. Please upgrade to Xcode 12.', -// ); -// devices = parseIOSDevicesList( -// execa.sync('xcrun', ['instruments', '-s']).stdout, -// ); -// } -// return devices; -// }; - const getSimulators = () => { let simulators: {devices: {[index: string]: Array}}; try {