From a6b4ab6dc3ff9673a8c53674f36c45c7bf32f0f9 Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Tue, 8 Nov 2022 15:02:43 +0100 Subject: [PATCH 01/13] feat: add buildIOS command --- docs/commands.md | 18 ++ .../src/commands/buildIOS/buildProject.ts | 161 ++++++++++ .../src/commands/buildIOS/index.ts | 287 ++++++++++++++++++ .../cli-platform-ios/src/commands/index.ts | 3 +- .../src/commands/runIOS/index.ts | 158 +--------- 5 files changed, 474 insertions(+), 153 deletions(-) create mode 100644 packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts create mode 100644 packages/cli-platform-ios/src/commands/buildIOS/index.ts diff --git a/docs/commands.md b/docs/commands.md index ce76268c3..da503c1a7 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -13,6 +13,7 @@ React Native CLI comes with following commands: - [`ram-bundle`](#ram-bundle) - [`run-android`](#run-android) - [`run-ios`](#run-ios) +- [`build-ios`](#build-ios) - [`start`](#start) - [`upgrade`](#upgrade) - [`profile-hermes`](#profile-hermes) @@ -389,6 +390,23 @@ react-native run-ios [options] Builds your app and starts it on iOS simulator. +### `build-ios` + +Usage: + +```sh +react-native build-ios [options] +``` + +Builds IOS app. + +#### Options + +#### `--mode ` + +> default: debug +Mode to build the app. Either 'debug' (default) or 'release'. + #### Options #### `--simulator ` diff --git a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts new file mode 100644 index 000000000..12a450f0e --- /dev/null +++ b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts @@ -0,0 +1,161 @@ +import child_process, { + ChildProcess, + SpawnOptionsWithoutStdio, +} from 'child_process'; +import chalk from 'chalk'; +import {IOSProjectInfo} from '@react-native-community/cli-types'; +import {logger, CLIError} from '@react-native-community/cli-tools'; +import ora from 'ora'; + +export type BuildFlags = { + configuration: string; + packager: boolean; + verbose: boolean; + xcconfig?: string; + buildFolder?: string; + port: number; + terminal: string | undefined; +}; + +export function buildProject( + xcodeProject: IOSProjectInfo, + udid: string, + scheme: string, + args: BuildFlags, +): Promise { + return new Promise((resolve, reject) => { + const xcodebuildArgs = [ + xcodeProject.isWorkspace ? '-workspace' : '-project', + xcodeProject.name, + ...(args.xcconfig ? ['-xcconfig', args.xcconfig] : []), + ...(args.buildFolder ? ['-derivedDataPath', args.buildFolder] : []), + '-configuration', + args.configuration, + '-scheme', + scheme, + '-destination', + `id=${udid}`, + ]; + // @todo use `getLoader` from cli-tools package + const loader = ora(); + logger.info( + `Building ${chalk.dim( + `(using "xcodebuild ${xcodebuildArgs.join(' ')}")`, + )}`, + ); + let xcodebuildOutputFormatter: ChildProcess | any; + if (!args.verbose) { + if (xcbeautifyAvailable()) { + xcodebuildOutputFormatter = child_process.spawn('xcbeautify', [], { + stdio: ['pipe', process.stdout, process.stderr], + }); + } else if (xcprettyAvailable()) { + xcodebuildOutputFormatter = child_process.spawn('xcpretty', [], { + stdio: ['pipe', process.stdout, process.stderr], + }); + } + } + const buildProcess = child_process.spawn( + 'xcodebuild', + xcodebuildArgs, + getProcessOptions(args), + ); + let buildOutput = ''; + let errorOutput = ''; + buildProcess.stdout.on('data', (data: Buffer) => { + const stringData = data.toString(); + buildOutput += stringData; + if (xcodebuildOutputFormatter) { + xcodebuildOutputFormatter.stdin.write(data); + } else { + if (logger.isVerbose()) { + logger.debug(stringData); + } else { + loader.start( + `Building the app${'.'.repeat(buildOutput.length % 10)}`, + ); + } + } + }); + logger.info('test ', JSON.stringify(args)); + buildProcess.stderr.on('data', (data: Buffer) => { + errorOutput += data; + }); + buildProcess.on('close', (code: number) => { + if (xcodebuildOutputFormatter) { + xcodebuildOutputFormatter.stdin.end(); + } else { + loader.stop(); + } + if (code !== 0) { + reject( + new CLIError( + ` + Failed to build iOS project. + + We ran "xcodebuild" command but it exited with error code ${code}. To debug build + logs further, consider building your app with Xcode.app, by opening + ${xcodeProject.name}. + `, + xcodebuildOutputFormatter + ? undefined + : buildOutput + '\n' + errorOutput, + ), + ); + return; + } + logger.success('Successfully built the app'); + resolve(buildOutput); + }); + }); +} + +function xcbeautifyAvailable() { + try { + child_process.execSync('xcbeautify --version', { + stdio: [0, 'pipe', 'ignore'], + }); + } catch (error) { + return false; + } + return true; +} + +function xcprettyAvailable() { + try { + child_process.execSync('xcpretty --version', { + stdio: [0, 'pipe', 'ignore'], + }); + } catch (error) { + return false; + } + return true; +} + +function getProcessOptions({ + packager, + terminal, + port, +}: { + packager: boolean; + terminal: string | undefined; + port: number; +}): SpawnOptionsWithoutStdio { + if (packager) { + return { + env: { + ...process.env, + RCT_TERMINAL: terminal, + RCT_METRO_PORT: port.toString(), + }, + }; + } + + return { + env: { + ...process.env, + RCT_TERMINAL: terminal, + RCT_NO_LAUNCH_PACKAGER: 'true', + }, + }; +} diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts new file mode 100644 index 000000000..845225aa3 --- /dev/null +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -0,0 +1,287 @@ +/** + * 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 child_process from 'child_process'; +import path from 'path'; +import chalk from 'chalk'; +import {Config} from '@react-native-community/cli-types'; +import { + logger, + CLIError, + getDefaultUserTerminal, +} from '@react-native-community/cli-tools'; +import execa from 'execa'; +import parseXctraceIOSDevicesList from '../runIOS/parseXctraceIOSDevicesList'; +import parseIOSDevicesList from '../runIOS/parseIOSDevicesList'; +import {Device} from '../../types'; +import {buildProject} from './buildProject'; +import findMatchingSimulator from '../runIOS/findMatchingSimulator'; + +type FlagsT = { + configuration: string; + simulator?: string; + scheme?: string; + device?: string | true; + udid?: string; + verbose: boolean; + port: number; + terminal: string | undefined; + xcconfig?: string; + buildFolder?: string; +}; + +function buildIOS(_: Array, ctx: Config, args: FlagsT) { + console.log(JSON.stringify(args)); + if (!ctx.project.ios) { + throw new CLIError( + 'iOS project folder not found. Are you sure this is a React Native project?', + ); + } + + const {xcodeProject, sourceDir} = ctx.project.ios; + + process.chdir(sourceDir); + + if (!xcodeProject) { + throw new CLIError( + `Could not find Xcode project files in "${sourceDir}" folder`, + ); + } + + const inferredSchemeName = path.basename( + xcodeProject.name, + path.extname(xcodeProject.name), + ); + const scheme = args.scheme || inferredSchemeName; + + logger.info( + `Found Xcode ${ + xcodeProject.isWorkspace ? 'workspace' : 'project' + } "${chalk.bold(xcodeProject.name)}"`, + ); + + const extendedArgs = { + ...args, + packager: false, + }; + + // // No need to load all available devices + if (!args.device && !args.udid) { + const selectedSimulator = getSelectedSimulator(args); + return buildProject( + xcodeProject, + selectedSimulator.udid, + scheme, + extendedArgs, + ); + } + + if (args.device && args.udid) { + return logger.error( + 'The `device` and `udid` options are mutually exclusive.', + ); + } + + 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, + ); + } + + if (args.udid) { + const device = devices.find((d) => d.udid === args.udid); + if (!device) { + return logger.error( + `Could not find a device with udid: "${chalk.bold( + args.udid, + )}". ${printFoundDevices(devices)}`, + ); + } + + return buildProject(xcodeProject, device.udid, scheme, extendedArgs); + } else { + const physicalDevices = devices.filter((d) => d.type !== 'simulator'); + const device = matchingDevice(physicalDevices, args.device); + if (device) { + return buildProject(xcodeProject, device.udid, scheme, extendedArgs); + } + } +} + +function matchingDevice( + devices: Array, + deviceName: string | true | undefined, +) { + if (deviceName === true) { + const firstIOSDevice = devices.find((d) => d.type === 'device')!; + if (firstIOSDevice) { + logger.info( + `Using first available device named "${chalk.bold( + firstIOSDevice.name, + )}" due to lack of name supplied.`, + ); + return firstIOSDevice; + } else { + logger.error('No iOS devices connected.'); + return undefined; + } + } + const deviceByName = devices.find( + (device) => + device.name === deviceName || formattedDeviceName(device) === deviceName, + ); + if (!deviceByName) { + logger.error( + `Could not find a device named: "${chalk.bold( + String(deviceName), + )}". ${printFoundDevices(devices)}`, + ); + } + return deviceByName; +} + +function getSelectedSimulator(args: FlagsT) { + let simulators: {devices: {[index: string]: Array}}; + try { + simulators = JSON.parse( + child_process.execFileSync( + 'xcrun', + ['simctl', 'list', '--json', 'devices'], + {encoding: 'utf8'}, + ), + ); + } catch (error) { + throw new CLIError( + 'Could not get the simulator list from Xcode. Please open Xcode and try running project directly from there to resolve the remaining issues.', + error, + ); + } + + /** + * If provided simulator does not exist, try simulators in following order + * - iPhone 14 + * - iPhone 13 + * - iPhone 12 + * - iPhone 11 + */ + const fallbackSimulators = [ + 'iPhone 14', + 'iPhone 13', + 'iPhone 12', + 'iPhone 11', + ]; + const selectedSimulator = fallbackSimulators.reduce((simulator, fallback) => { + return ( + simulator || findMatchingSimulator(simulators, {simulator: fallback}) + ); + }, findMatchingSimulator(simulators, args)); + + if (!selectedSimulator) { + throw new CLIError( + `No simulator available with ${ + args.simulator ? `name "${args.simulator}"` : `udid "${args.udid}"` + }`, + ); + } + return selectedSimulator; +} + +function formattedDeviceName(simulator: Device) { + return simulator.version + ? `${simulator.name} (${simulator.version})` + : simulator.name; +} + +function printFoundDevices(devices: Array) { + return [ + 'Available devices:', + ...devices.map((device) => ` - ${device.name} (${device.udid})`), + ].join('\n'); +} + +export default { + name: 'build-ios', + description: 'builds your app on iOS simulator', + func: buildIOS, + examples: [ + { + desc: 'Run on a different simulator, e.g. iPhone SE (2nd generation)', + cmd: 'react-native run-ios --simulator "iPhone SE (2nd generation)"', + }, + { + desc: "Run on a connected device, e.g. Max's iPhone", + cmd: 'react-native run-ios --device "Max\'s iPhone"', + }, + { + desc: 'Run on the AppleTV simulator', + cmd: + 'react-native run-ios --simulator "Apple TV" --scheme "helloworld-tvOS"', + }, + ], + options: [ + { + name: '--simulator ', + description: + 'Explicitly set simulator to use. Optionally include iOS version between ' + + 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"', + default: 'iPhone 14', + }, + { + name: '--configuration ', + description: 'Explicitly set the scheme configuration to use', + default: 'Debug', + }, + { + name: '--scheme ', + description: 'Explicitly set Xcode scheme to use', + }, + { + name: '--device [string]', + description: + 'Explicitly set device to use by name. The value is not required if you have a single device connected.', + }, + { + name: '--udid ', + description: 'Explicitly set device to use by udid', + }, + { + name: '--verbose', + description: 'Do not use xcbeautify or xcpretty even if installed', + }, + { + name: '--port ', + default: process.env.RCT_METRO_PORT || 8081, + parse: Number, + }, + { + name: '--terminal ', + description: + 'Launches the Metro Bundler in a new window using the specified terminal path.', + default: getDefaultUserTerminal, + }, + { + name: '--xcconfig [string]', + description: 'Explicitly set xcconfig to use', + }, + { + name: '--buildFolder ', + description: + 'Location for iOS build artifacts. Corresponds to Xcode\'s "-derivedDataPath".', + }, + ], +}; diff --git a/packages/cli-platform-ios/src/commands/index.ts b/packages/cli-platform-ios/src/commands/index.ts index 2dc3c640e..e091b130a 100644 --- a/packages/cli-platform-ios/src/commands/index.ts +++ b/packages/cli-platform-ios/src/commands/index.ts @@ -1,4 +1,5 @@ import logIOS from './logIOS'; import runIOS from './runIOS'; +import buildIOS from './buildIOS'; -export default [logIOS, runIOS]; +export default [logIOS, runIOS, buildIOS]; diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 99fe43cf2..e4daf32a6 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -6,25 +6,21 @@ * */ -import child_process, { - ChildProcess, - // @ts-ignore - SpawnOptionsWithoutStdio, -} from 'child_process'; +import child_process from '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 execa from 'execa'; import { logger, CLIError, getDefaultUserTerminal, } from '@react-native-community/cli-tools'; +import parseIOSDevicesList from './parseIOSDevicesList'; +import parseXctraceIOSDevicesList from './parseXctraceIOSDevicesList'; +import findMatchingSimulator from './findMatchingSimulator'; +import {buildProject} from '../buildIOS/buildProject'; import {Device} from '../../types'; -import ora from 'ora'; -import execa from 'execa'; type FlagsT = { simulator?: string; @@ -310,98 +306,6 @@ async function runOnDevice( return logger.success('Installed the app on the device.'); } -function buildProject( - xcodeProject: IOSProjectInfo, - udid: string | undefined, - scheme: string, - args: FlagsT, -): Promise { - return new Promise((resolve, reject) => { - const xcodebuildArgs = [ - xcodeProject.isWorkspace ? '-workspace' : '-project', - xcodeProject.name, - ...(args.xcconfig ? ['-xcconfig', args.xcconfig] : []), - ...(args.buildFolder ? ['-derivedDataPath', args.buildFolder] : []), - '-configuration', - args.configuration, - '-scheme', - scheme, - '-destination', - `id=${udid}`, - ]; - // @todo use `getLoader` from cli-tools package - const loader = ora(); - logger.info( - `Building ${chalk.dim( - `(using "xcodebuild ${xcodebuildArgs.join(' ')}")`, - )}`, - ); - let xcodebuildOutputFormatter: ChildProcess | any; - if (!args.verbose) { - if (xcbeautifyAvailable()) { - xcodebuildOutputFormatter = child_process.spawn('xcbeautify', [], { - stdio: ['pipe', process.stdout, process.stderr], - }); - } else if (xcprettyAvailable()) { - xcodebuildOutputFormatter = child_process.spawn('xcpretty', [], { - stdio: ['pipe', process.stdout, process.stderr], - }); - } - } - const buildProcess = child_process.spawn( - 'xcodebuild', - xcodebuildArgs, - getProcessOptions(args), - ); - let buildOutput = ''; - let errorOutput = ''; - buildProcess.stdout.on('data', (data: Buffer) => { - const stringData = data.toString(); - buildOutput += stringData; - if (xcodebuildOutputFormatter) { - xcodebuildOutputFormatter.stdin.write(data); - } else { - if (logger.isVerbose()) { - logger.debug(stringData); - } else { - loader.start( - `Building the app${'.'.repeat(buildOutput.length % 10)}`, - ); - } - } - }); - buildProcess.stderr.on('data', (data: Buffer) => { - errorOutput += data; - }); - buildProcess.on('close', (code: number) => { - if (xcodebuildOutputFormatter) { - xcodebuildOutputFormatter.stdin.end(); - } else { - loader.stop(); - } - if (code !== 0) { - reject( - new CLIError( - ` - Failed to build iOS project. - - We ran "xcodebuild" command but it exited with error code ${code}. To debug build - logs further, consider building your app with Xcode.app, by opening - ${xcodeProject.name}. - `, - xcodebuildOutputFormatter - ? undefined - : buildOutput + '\n' + errorOutput, - ), - ); - return; - } - logger.success('Successfully built the app'); - resolve(buildOutput); - }); - }); -} - function bootSimulator(selectedSimulator: Device) { const simulatorFullName = formattedDeviceName(selectedSimulator); logger.info(`Launching ${simulatorFullName}`); @@ -478,28 +382,6 @@ function getPlatformName(buildOutput: string) { return platformNameMatch[1]; } -function xcbeautifyAvailable() { - try { - child_process.execSync('xcbeautify --version', { - stdio: [0, 'pipe', 'ignore'], - }); - } catch (error) { - return false; - } - return true; -} - -function xcprettyAvailable() { - try { - child_process.execSync('xcpretty --version', { - stdio: [0, 'pipe', 'ignore'], - }); - } catch (error) { - return false; - } - return true; -} - function matchingDevice( devices: Array, deviceName: string | true | undefined, @@ -545,34 +427,6 @@ function printFoundDevices(devices: Array) { ].join('\n'); } -function getProcessOptions({ - packager, - terminal, - port, -}: { - packager: boolean; - terminal: string | undefined; - port: number; -}): SpawnOptionsWithoutStdio { - if (packager) { - return { - env: { - ...process.env, - RCT_TERMINAL: terminal, - RCT_METRO_PORT: port.toString(), - }, - }; - } - - return { - env: { - ...process.env, - RCT_TERMINAL: terminal, - RCT_NO_LAUNCH_PACKAGER: 'true', - }, - }; -} - export default { name: 'run-ios', description: 'builds your app and starts it on iOS simulator', From 51b47869505c50aefeb8f6d5031bff8b5e138710 Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 9 Nov 2022 13:44:48 +0100 Subject: [PATCH 02/13] refactor: extract shared functions from build-ios --- .../src/commands/buildIOS/buildProject.ts | 2 +- .../src/commands/buildIOS/index.ts | 86 ++----- .../src/commands/runIOS/index.ts | 6 +- .../__tests__/findMatchingSimulator.test.ts | 0 .../src/tools/__tests__/getDevices.test.ts | 221 ++++++++++++++++++ .../__tests__/parseIOSDevicesList.test.ts | 0 .../parseXctraceIOSDevicesList.test.ts | 0 .../runIOS => tools}/findMatchingSimulator.ts | 2 +- .../src/tools/getDestinationSimulator.ts | 45 ++++ .../cli-platform-ios/src/tools/getDevices.ts | 22 ++ .../runIOS => tools}/parseIOSDevicesList.ts | 2 +- .../parseXctraceIOSDevicesList.ts | 2 +- 12 files changed, 314 insertions(+), 74 deletions(-) rename packages/cli-platform-ios/src/{commands/runIOS => tools}/__tests__/findMatchingSimulator.test.ts (100%) create mode 100644 packages/cli-platform-ios/src/tools/__tests__/getDevices.test.ts rename packages/cli-platform-ios/src/{commands/runIOS => tools}/__tests__/parseIOSDevicesList.test.ts (100%) rename packages/cli-platform-ios/src/{commands/runIOS => tools}/__tests__/parseXctraceIOSDevicesList.test.ts (100%) rename packages/cli-platform-ios/src/{commands/runIOS => tools}/findMatchingSimulator.ts (99%) create mode 100644 packages/cli-platform-ios/src/tools/getDestinationSimulator.ts create mode 100644 packages/cli-platform-ios/src/tools/getDevices.ts rename packages/cli-platform-ios/src/{commands/runIOS => tools}/parseIOSDevicesList.ts (97%) rename packages/cli-platform-ios/src/{commands/runIOS => tools}/parseXctraceIOSDevicesList.ts (97%) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts index 12a450f0e..6ea4236e0 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts @@ -77,7 +77,7 @@ export function buildProject( } } }); - logger.info('test ', JSON.stringify(args)); + buildProcess.stderr.on('data', (data: Buffer) => { errorOutput += data; }); diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index 845225aa3..651e1c3cf 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -6,7 +6,6 @@ * */ -import child_process from 'child_process'; import path from 'path'; import chalk from 'chalk'; import {Config} from '@react-native-community/cli-types'; @@ -15,12 +14,10 @@ import { CLIError, getDefaultUserTerminal, } from '@react-native-community/cli-tools'; -import execa from 'execa'; -import parseXctraceIOSDevicesList from '../runIOS/parseXctraceIOSDevicesList'; -import parseIOSDevicesList from '../runIOS/parseIOSDevicesList'; import {Device} from '../../types'; import {buildProject} from './buildProject'; -import findMatchingSimulator from '../runIOS/findMatchingSimulator'; +import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; +import {getDevices} from '../../tools/getDevices'; type FlagsT = { configuration: string; @@ -72,7 +69,22 @@ function buildIOS(_: Array, ctx: Config, args: FlagsT) { // // No need to load all available devices if (!args.device && !args.udid) { - const selectedSimulator = getSelectedSimulator(args); + /** + * If provided simulator does not exist, try simulators in following order + * - iPhone 14 + * - iPhone 13 + * - iPhone 12 + * - iPhone 11 + */ + const fallbackSimulators = [ + 'iPhone 14', + 'iPhone 13', + 'iPhone 12', + 'iPhone 11', + ]; + + const selectedSimulator = getDestinationSimulator(args, fallbackSimulators); + return buildProject( xcodeProject, selectedSimulator.udid, @@ -87,21 +99,7 @@ function buildIOS(_: 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 = getDevices(); if (args.udid) { const device = devices.find((d) => d.udid === args.udid); @@ -155,52 +153,6 @@ function matchingDevice( return deviceByName; } -function getSelectedSimulator(args: FlagsT) { - let simulators: {devices: {[index: string]: Array}}; - try { - simulators = JSON.parse( - child_process.execFileSync( - 'xcrun', - ['simctl', 'list', '--json', 'devices'], - {encoding: 'utf8'}, - ), - ); - } catch (error) { - throw new CLIError( - 'Could not get the simulator list from Xcode. Please open Xcode and try running project directly from there to resolve the remaining issues.', - error, - ); - } - - /** - * If provided simulator does not exist, try simulators in following order - * - iPhone 14 - * - iPhone 13 - * - iPhone 12 - * - iPhone 11 - */ - const fallbackSimulators = [ - 'iPhone 14', - 'iPhone 13', - 'iPhone 12', - 'iPhone 11', - ]; - const selectedSimulator = fallbackSimulators.reduce((simulator, fallback) => { - return ( - simulator || findMatchingSimulator(simulators, {simulator: fallback}) - ); - }, findMatchingSimulator(simulators, args)); - - if (!selectedSimulator) { - throw new CLIError( - `No simulator available with ${ - args.simulator ? `name "${args.simulator}"` : `udid "${args.udid}"` - }`, - ); - } - return selectedSimulator; -} - function formattedDeviceName(simulator: Device) { return simulator.version ? `${simulator.name} (${simulator.version})` diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index e4daf32a6..b9ef24847 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -16,9 +16,9 @@ import { CLIError, getDefaultUserTerminal, } from '@react-native-community/cli-tools'; -import parseIOSDevicesList from './parseIOSDevicesList'; -import parseXctraceIOSDevicesList from './parseXctraceIOSDevicesList'; -import findMatchingSimulator from './findMatchingSimulator'; +import parseIOSDevicesList from '../../tools/parseIOSDevicesList'; +import parseXctraceIOSDevicesList from '../../tools/parseXctraceIOSDevicesList'; +import findMatchingSimulator from '../../tools/findMatchingSimulator'; import {buildProject} from '../buildIOS/buildProject'; import {Device} from '../../types'; diff --git a/packages/cli-platform-ios/src/commands/runIOS/__tests__/findMatchingSimulator.test.ts b/packages/cli-platform-ios/src/tools/__tests__/findMatchingSimulator.test.ts similarity index 100% rename from packages/cli-platform-ios/src/commands/runIOS/__tests__/findMatchingSimulator.test.ts rename to packages/cli-platform-ios/src/tools/__tests__/findMatchingSimulator.test.ts diff --git a/packages/cli-platform-ios/src/tools/__tests__/getDevices.test.ts b/packages/cli-platform-ios/src/tools/__tests__/getDevices.test.ts new file mode 100644 index 000000000..5faadab0b --- /dev/null +++ b/packages/cli-platform-ios/src/tools/__tests__/getDevices.test.ts @@ -0,0 +1,221 @@ +/** + * 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 execa from 'execa'; +import {getDevices} from '../getDevices'; + +jest.dontMock('../getDevices'); + +jest.mock('execa', () => { + return {sync: jest.fn()}; +}); + +const expectedOutput = { + xctraceListLatest: { + stdout: [ + '== Devices ==', + 'Maxs MacBook Pro (11111111-1111-1111-1111-111111111111)', + "Max's iPhone (9.2) (00008030-000D19512210802E)", + 'other-iphone (9.2) (72a186ccfd93472a186ccfd934)', + '', + '== Simulators ==', + 'iPad 2 (9.3) (07538CE4-675B-4EDA-90F2-3DD3CD93309D)', + 'iPad Air (9.3) (0745F6D1-6DC5-4427-B9A6-6FBA327ED65A)', + 'iPhone 6s (9.3) (3DBE4ECF-9A86-469E-921B-EE0F9C9AB8F4)', + 'Known Templates:', + 'Activity Monitor', + 'Blank', + 'System Usage', + 'Zombies', + ].join('\n'), + stderr: '', + }, + xctraceListOld: { + stderr: [ + '== Devices ==', + 'Maxs MacBook Pro (11111111-1111-1111-1111-111111111111)', + "Max's iPhone (9.2) (00008030-000D19512210802E)", + 'other-iphone (9.2) (72a186ccfd93472a186ccfd934)', + '', + '== Simulators ==', + 'iPad 2 (9.3) (07538CE4-675B-4EDA-90F2-3DD3CD93309D)', + 'iPad Air (9.3) (0745F6D1-6DC5-4427-B9A6-6FBA327ED65A)', + 'iPhone 6s (9.3) (3DBE4ECF-9A86-469E-921B-EE0F9C9AB8F4)', + 'Known Templates:', + 'Activity Monitor', + 'Blank', + 'System Usage', + 'Zombies', + ].join('\n'), + }, + depracatedList: { + stdout: [ + 'Known Devices:', + 'Maxs MacBook Pro [11111111-1111-1111-1111-111111111111]', + "Max's iPhone (9.2) [00008030-000D19512210802E]", + 'other-iphone (9.2) [72a186ccfd93472a186ccfd934]', + 'iPad 2 (9.3) [07538CE4-675B-4EDA-90F2-3DD3CD93309D] (Simulator)', + 'iPad Air (9.3) [0745F6D1-6DC5-4427-B9A6-6FBA327ED65A] (Simulator)', + 'iPhone 6s (9.3) [3DBE4ECF-9A86-469E-921B-EE0F9C9AB8F4] (Simulator)', + 'Known Templates:', + 'Activity Monitor', + 'Blank', + 'System Usage', + 'Zombies', + ].join('\n'), + stderr: '', + }, +}; + +describe('getDevices', () => { + it('parses typical output for xctrace list for xcode 12.5+', () => { + (execa.sync as jest.Mock).mockReturnValueOnce( + expectedOutput.xctraceListLatest, + ); + const devices = getDevices(); + + expect(devices).toEqual([ + { + name: 'Maxs MacBook Pro', + udid: '11111111-1111-1111-1111-111111111111', + type: 'catalyst', + }, + { + name: "Max's iPhone", + udid: '00008030-000D19512210802E', + version: '9.2', + type: 'device', + }, + { + name: 'other-iphone', + type: 'device', + udid: '72a186ccfd93472a186ccfd934', + version: '9.2', + }, + { + name: 'iPad 2', + udid: '07538CE4-675B-4EDA-90F2-3DD3CD93309D', + version: '9.3', + type: 'simulator', + }, + { + name: 'iPad Air', + udid: '0745F6D1-6DC5-4427-B9A6-6FBA327ED65A', + version: '9.3', + type: 'simulator', + }, + { + name: 'iPhone 6s', + udid: '3DBE4ECF-9A86-469E-921B-EE0F9C9AB8F4', + version: '9.3', + type: 'simulator', + }, + ]); + }); + + it('parses typical output for xctrace list for xcode upto 12.5', () => { + (execa.sync as jest.Mock).mockReturnValueOnce( + expectedOutput.xctraceListOld, + ); + const devices = getDevices(); + + expect(devices).toEqual([ + { + name: 'Maxs MacBook Pro', + udid: '11111111-1111-1111-1111-111111111111', + type: 'catalyst', + }, + { + name: "Max's iPhone", + udid: '00008030-000D19512210802E', + version: '9.2', + type: 'device', + }, + { + name: 'other-iphone', + type: 'device', + udid: '72a186ccfd93472a186ccfd934', + version: '9.2', + }, + { + name: 'iPad 2', + udid: '07538CE4-675B-4EDA-90F2-3DD3CD93309D', + version: '9.3', + type: 'simulator', + }, + { + name: 'iPad Air', + udid: '0745F6D1-6DC5-4427-B9A6-6FBA327ED65A', + version: '9.3', + type: 'simulator', + }, + { + name: 'iPhone 6s', + udid: '3DBE4ECF-9A86-469E-921B-EE0F9C9AB8F4', + version: '9.3', + type: 'simulator', + }, + ]); + }); + + it('parses typical output for deprecated list', () => { + (execa.sync as jest.Mock).mockImplementation((_, [command]) => { + if (command === 'xctrace') { + throw new Error('some error'); + } + return expectedOutput.depracatedList; + }).mock; + const devices = getDevices(); + + expect(devices).toEqual([ + { + name: 'Maxs MacBook Pro', + udid: '11111111-1111-1111-1111-111111111111', + type: 'catalyst', + }, + { + name: "Max's iPhone", + udid: '00008030-000D19512210802E', + version: '9.2', + type: 'device', + }, + { + name: 'other-iphone', + type: 'device', + udid: '72a186ccfd93472a186ccfd934', + version: '9.2', + }, + { + name: 'iPad 2', + udid: '07538CE4-675B-4EDA-90F2-3DD3CD93309D', + version: '9.3', + type: 'simulator', + }, + { + name: 'iPad Air', + udid: '0745F6D1-6DC5-4427-B9A6-6FBA327ED65A', + version: '9.3', + type: 'simulator', + }, + { + name: 'iPhone 6s', + udid: '3DBE4ECF-9A86-469E-921B-EE0F9C9AB8F4', + version: '9.3', + type: 'simulator', + }, + ]); + }); + + it('ignores garbage', () => { + (execa.sync as jest.Mock).mockReturnValueOnce({ + stdout: 'Something went terribly wrong (-42)', + stderr: '', + }); + expect(getDevices()).toEqual([]); + }); +}); diff --git a/packages/cli-platform-ios/src/commands/runIOS/__tests__/parseIOSDevicesList.test.ts b/packages/cli-platform-ios/src/tools/__tests__/parseIOSDevicesList.test.ts similarity index 100% rename from packages/cli-platform-ios/src/commands/runIOS/__tests__/parseIOSDevicesList.test.ts rename to packages/cli-platform-ios/src/tools/__tests__/parseIOSDevicesList.test.ts diff --git a/packages/cli-platform-ios/src/commands/runIOS/__tests__/parseXctraceIOSDevicesList.test.ts b/packages/cli-platform-ios/src/tools/__tests__/parseXctraceIOSDevicesList.test.ts similarity index 100% rename from packages/cli-platform-ios/src/commands/runIOS/__tests__/parseXctraceIOSDevicesList.test.ts rename to packages/cli-platform-ios/src/tools/__tests__/parseXctraceIOSDevicesList.test.ts diff --git a/packages/cli-platform-ios/src/commands/runIOS/findMatchingSimulator.ts b/packages/cli-platform-ios/src/tools/findMatchingSimulator.ts similarity index 99% rename from packages/cli-platform-ios/src/commands/runIOS/findMatchingSimulator.ts rename to packages/cli-platform-ios/src/tools/findMatchingSimulator.ts index 6041bf8ff..e0a51a662 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/findMatchingSimulator.ts +++ b/packages/cli-platform-ios/src/tools/findMatchingSimulator.ts @@ -6,7 +6,7 @@ * */ -import {Device} from '../../types'; +import {Device} from '../types'; /** * Takes in a parsed simulator list and a desired name, and returns an object with the matching simulator. The desired diff --git a/packages/cli-platform-ios/src/tools/getDestinationSimulator.ts b/packages/cli-platform-ios/src/tools/getDestinationSimulator.ts new file mode 100644 index 000000000..214b90634 --- /dev/null +++ b/packages/cli-platform-ios/src/tools/getDestinationSimulator.ts @@ -0,0 +1,45 @@ +import child_process from 'child_process'; +import {CLIError} from '@react-native-community/cli-tools'; +import {Device} from '../types'; +import findMatchingSimulator from './findMatchingSimulator'; + +type FlagsT = { + simulator?: string; + udid?: string; +}; + +export function getDestinationSimulator( + args: FlagsT, + fallbackSimulators: string[] = [], +) { + let simulators: {devices: {[index: string]: Array}}; + try { + simulators = JSON.parse( + child_process.execFileSync( + 'xcrun', + ['simctl', 'list', '--json', 'devices'], + {encoding: 'utf8'}, + ), + ); + } catch (error) { + throw new CLIError( + 'Could not get the simulator list from Xcode. Please open Xcode and try running project directly from there to resolve the remaining issues.', + error, + ); + } + + const selectedSimulator = fallbackSimulators.reduce((simulator, fallback) => { + return ( + simulator || findMatchingSimulator(simulators, {simulator: fallback}) + ); + }, findMatchingSimulator(simulators, args)); + + if (!selectedSimulator) { + throw new CLIError( + `No simulator available with ${ + args.simulator ? `name "${args.simulator}"` : `udid "${args.udid}"` + }`, + ); + } + return selectedSimulator; +} diff --git a/packages/cli-platform-ios/src/tools/getDevices.ts b/packages/cli-platform-ios/src/tools/getDevices.ts new file mode 100644 index 000000000..313c53e9a --- /dev/null +++ b/packages/cli-platform-ios/src/tools/getDevices.ts @@ -0,0 +1,22 @@ +import execa from 'execa'; +import {logger} from '@react-native-community/cli-tools'; +import parseIOSDevicesList from './parseIOSDevicesList'; +import parseXctraceIOSDevicesList from './parseXctraceIOSDevicesList'; +import {Device} from '../types'; + +export function getDevices(): Device[] { + try { + const out = execa.sync('xcrun', ['xctrace', 'list', 'devices']); + return 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.', + ); + return parseIOSDevicesList( + execa.sync('xcrun', ['instruments', '-s']).stdout, + ); + } +} diff --git a/packages/cli-platform-ios/src/commands/runIOS/parseIOSDevicesList.ts b/packages/cli-platform-ios/src/tools/parseIOSDevicesList.ts similarity index 97% rename from packages/cli-platform-ios/src/commands/runIOS/parseIOSDevicesList.ts rename to packages/cli-platform-ios/src/tools/parseIOSDevicesList.ts index b7255afdc..09c3509e1 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/parseIOSDevicesList.ts +++ b/packages/cli-platform-ios/src/tools/parseIOSDevicesList.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * */ -import {Device} from '../../types'; +import {Device} from '../types'; /** * Parses the output of the `xcrun instruments -s` command and returns metadata diff --git a/packages/cli-platform-ios/src/commands/runIOS/parseXctraceIOSDevicesList.ts b/packages/cli-platform-ios/src/tools/parseXctraceIOSDevicesList.ts similarity index 97% rename from packages/cli-platform-ios/src/commands/runIOS/parseXctraceIOSDevicesList.ts rename to packages/cli-platform-ios/src/tools/parseXctraceIOSDevicesList.ts index 22fa4dfeb..cf7bec464 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/parseXctraceIOSDevicesList.ts +++ b/packages/cli-platform-ios/src/tools/parseXctraceIOSDevicesList.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * */ -import {Device} from '../../types'; +import {Device} from '../types'; /** * Parses the output of the `xcrun instruments -s` command and returns metadata From 41ea7cc45b1cd4429197d542dd67ee49de84bee6 Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 9 Nov 2022 14:13:15 +0100 Subject: [PATCH 03/13] refactor: extract shared functions from run-ios --- .../src/commands/runIOS/index.ts | 52 ++----------------- 1 file changed, 4 insertions(+), 48 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index b9ef24847..6b51a9b6d 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -10,17 +10,15 @@ import child_process from 'child_process'; import path from 'path'; import chalk from 'chalk'; import {Config, IOSProjectInfo} from '@react-native-community/cli-types'; -import execa from 'execa'; import { logger, CLIError, getDefaultUserTerminal, } from '@react-native-community/cli-tools'; -import parseIOSDevicesList from '../../tools/parseIOSDevicesList'; -import parseXctraceIOSDevicesList from '../../tools/parseXctraceIOSDevicesList'; -import findMatchingSimulator from '../../tools/findMatchingSimulator'; import {buildProject} from '../buildIOS/buildProject'; import {Device} from '../../types'; +import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; +import {getDevices} from '../../tools/getDevices'; type FlagsT = { simulator?: string; @@ -77,21 +75,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 = getDevices(); if (args.udid) { const device = devices.find((d) => d.udid === args.udid); @@ -121,22 +105,6 @@ async function runOnSimulator( scheme: string, args: FlagsT, ) { - let simulators: {devices: {[index: string]: Array}}; - try { - simulators = JSON.parse( - child_process.execFileSync( - 'xcrun', - ['simctl', 'list', '--json', 'devices'], - {encoding: 'utf8'}, - ), - ); - } catch (error) { - throw new CLIError( - 'Could not get the simulator list from Xcode. Please open Xcode and try running project directly from there to resolve the remaining issues.', - error, - ); - } - /** * If provided simulator does not exist, try simulators in following order * - iPhone 14 @@ -150,19 +118,7 @@ async function runOnSimulator( 'iPhone 12', 'iPhone 11', ]; - const selectedSimulator = fallbackSimulators.reduce((simulator, fallback) => { - return ( - simulator || findMatchingSimulator(simulators, {simulator: fallback}) - ); - }, findMatchingSimulator(simulators, args)); - - if (!selectedSimulator) { - throw new CLIError( - `No simulator available with ${ - args.simulator ? `name "${args.simulator}"` : `udid "${args.udid}"` - }`, - ); - } + const selectedSimulator = getDestinationSimulator(args, fallbackSimulators); /** * Booting simulator through `xcrun simctl boot` will boot it in the `headless` mode From a725450da4375871053a89eeae502b88d48b48b8 Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 16 Nov 2022 15:54:30 +0100 Subject: [PATCH 04/13] feat: default build ios to generic destination --- .../src/commands/buildIOS/buildProject.ts | 8 ++++++-- packages/cli-platform-ios/src/commands/buildIOS/index.ts | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts index 6ea4236e0..9de1f7940 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts @@ -19,7 +19,7 @@ export type BuildFlags = { export function buildProject( xcodeProject: IOSProjectInfo, - udid: string, + udid: string | undefined, scheme: string, args: BuildFlags, ): Promise { @@ -34,7 +34,11 @@ export function buildProject( '-scheme', scheme, '-destination', - `id=${udid}`, + udid + ? `id=${udid}` + : args.configuration === 'Debug' + ? 'generic/platform=iOS Simulator' + : 'generic/platform=iOS', ]; // @todo use `getLoader` from cli-tools package const loader = ora(); diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index 651e1c3cf..8ef53e71c 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -22,9 +22,9 @@ import {getDevices} from '../../tools/getDevices'; type FlagsT = { configuration: string; simulator?: string; - scheme?: string; device?: string | true; udid?: string; + scheme?: string; verbose: boolean; port: number; terminal: string | undefined; @@ -69,6 +69,10 @@ function buildIOS(_: Array, ctx: Config, args: FlagsT) { // // No need to load all available devices if (!args.device && !args.udid) { + if (!args.simulator) { + return buildProject(xcodeProject, undefined, scheme, extendedArgs); + } + /** * If provided simulator does not exist, try simulators in following order * - iPhone 14 @@ -191,7 +195,6 @@ export default { description: 'Explicitly set simulator to use. Optionally include iOS version between ' + 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"', - default: 'iPhone 14', }, { name: '--configuration ', From 03800e18e5eefd594ed3d67c1d4a8c3a0d172bca Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 16 Nov 2022 16:09:35 +0100 Subject: [PATCH 05/13] feat: update build-ios examples --- .../src/commands/buildIOS/index.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index 8ef53e71c..0e7b9bde5 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -33,7 +33,6 @@ type FlagsT = { }; function buildIOS(_: Array, ctx: Config, args: FlagsT) { - console.log(JSON.stringify(args)); if (!ctx.project.ios) { throw new CLIError( 'iOS project folder not found. Are you sure this is a React Native project?', @@ -176,17 +175,16 @@ export default { func: buildIOS, examples: [ { - desc: 'Run on a different simulator, e.g. iPhone SE (2nd generation)', - cmd: 'react-native run-ios --simulator "iPhone SE (2nd generation)"', + desc: 'Build the app for the IOS simulator', + cmd: 'react-native build-ios', }, { - desc: "Run on a connected device, e.g. Max's iPhone", - cmd: 'react-native run-ios --device "Max\'s iPhone"', + desc: 'Build the app for all IOS devices', + cmd: 'react-native build-ios --configuration "Release"', }, { - desc: 'Run on the AppleTV simulator', - cmd: - 'react-native run-ios --simulator "Apple TV" --scheme "helloworld-tvOS"', + desc: 'Build the app for a specific IOS device', + cmd: 'react-native build-ios --simulator "IPhone 11"', }, ], options: [ From ae4228bb8cc16a5a15cfcc0260f5fdd43bfa8dcb Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 16 Nov 2022 16:15:48 +0100 Subject: [PATCH 06/13] docs: update build-ios docs --- docs/commands.md | 70 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index da503c1a7..414723ca5 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -390,6 +390,68 @@ react-native run-ios [options] Builds your app and starts it on iOS simulator. + +#### Options + +#### `--simulator ` + +> default: iPhone 14 + +Explicitly set the simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version, e.g. `"iPhone 6 (10.0)"`. + +Notes: If selected simulator does not exist, cli will try to run fallback simulators in following order: + +- `iPhone 14` +- `iPhone 13` +- `iPhone 12` +- `iPhone 11` + +Notes: `simulator_name` must be a valid iOS simulator name. If in doubt, open your AwesomeApp/ios/AwesomeApp.xcodeproj folder on XCode and unroll the dropdown menu containing the simulator list. The dropdown menu is situated on the right hand side of the play button (top left corner). + +Example: this will launch your project directly onto the iPhone XS Max simulator: + +```sh +react-native run-ios --simulator "iPhone XS Max" +``` + +#### `--configuration ` + +Explicitly set the scheme configuration to use default: 'Debug'. + +#### `--scheme ` + +Explicitly set Xcode scheme to use. + +#### `--device [string]` + +Explicitly set device to use by name. The value is not required if you have a single device connected. + +#### `--udid ` + +Explicitly set device to use by udid. + +#### `--no-packager` + +Do not launch packager while building. + +#### `--verbose` + +Do not use `xcbeautify` or `xcpretty` even if installed. + +#### `--port ` + +Runs packager on specified port. + +Default: `process.env.RCT_METRO_PORT || 8081` + +#### `--xcconfig ` + +Explicitly pass `xcconfig` options from the command line. + +#### `--buildFolder ` + +Location for iOS build artifacts. Corresponds to Xcode's `-derivedDataPath`. + ### `build-ios` Usage: @@ -401,14 +463,6 @@ react-native build-ios [options] Builds IOS app. #### Options - -#### `--mode ` - -> default: debug -Mode to build the app. Either 'debug' (default) or 'release'. - -#### Options - #### `--simulator ` > default: iPhone 14 From 198e75c69f12c927a713274f1be61731ff82d53b Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 23 Nov 2022 16:35:24 +0100 Subject: [PATCH 07/13] refactor: use shared build flags --- .../cli-platform-ios/src/commands/buildIOS/index.ts | 12 +++--------- .../cli-platform-ios/src/commands/runIOS/index.ts | 13 +++---------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index 0e7b9bde5..d9d19081e 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -15,22 +15,16 @@ import { getDefaultUserTerminal, } from '@react-native-community/cli-tools'; import {Device} from '../../types'; -import {buildProject} from './buildProject'; +import {BuildFlags, buildProject} from './buildProject'; import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; import {getDevices} from '../../tools/getDevices'; -type FlagsT = { - configuration: string; +interface FlagsT extends BuildFlags { simulator?: string; device?: string | true; udid?: string; scheme?: string; - verbose: boolean; - port: number; - terminal: string | undefined; - xcconfig?: string; - buildFolder?: string; -}; +} function buildIOS(_: Array, ctx: Config, args: FlagsT) { if (!ctx.project.ios) { diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 6b51a9b6d..89e3991d7 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -15,25 +15,18 @@ import { CLIError, getDefaultUserTerminal, } from '@react-native-community/cli-tools'; -import {buildProject} from '../buildIOS/buildProject'; +import {BuildFlags, buildProject} from '../buildIOS/buildProject'; import {Device} from '../../types'; import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; import {getDevices} from '../../tools/getDevices'; -type FlagsT = { +interface FlagsT extends BuildFlags { simulator?: string; - configuration: string; scheme?: string; projectPath: string; device?: string | true; udid?: string; - packager: boolean; - verbose: boolean; - port: number; - terminal: string | undefined; - xcconfig?: string; - buildFolder?: string; -}; +} function runIOS(_: Array, ctx: Config, args: FlagsT) { if (!ctx.project.ios) { From 6ba04489c29835657b4b1e8e5b4ebc2d15c0264b Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 23 Nov 2022 21:48:27 +0100 Subject: [PATCH 08/13] feat: rename configuratio to mode --- .../src/commands/buildIOS/buildProject.ts | 6 +++--- .../cli-platform-ios/src/commands/buildIOS/index.ts | 6 +++--- .../cli-platform-ios/src/commands/runIOS/index.ts | 13 ++++--------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts index 9de1f7940..c23b0502b 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts @@ -8,7 +8,7 @@ import {logger, CLIError} from '@react-native-community/cli-tools'; import ora from 'ora'; export type BuildFlags = { - configuration: string; + mode: string; packager: boolean; verbose: boolean; xcconfig?: string; @@ -30,13 +30,13 @@ export function buildProject( ...(args.xcconfig ? ['-xcconfig', args.xcconfig] : []), ...(args.buildFolder ? ['-derivedDataPath', args.buildFolder] : []), '-configuration', - args.configuration, + args.mode, '-scheme', scheme, '-destination', udid ? `id=${udid}` - : args.configuration === 'Debug' + : args.mode === 'Debug' ? 'generic/platform=iOS Simulator' : 'generic/platform=iOS', ]; diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index d9d19081e..8561e2d7e 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -174,7 +174,7 @@ export default { }, { desc: 'Build the app for all IOS devices', - cmd: 'react-native build-ios --configuration "Release"', + cmd: 'react-native build-ios --mode "Release"', }, { desc: 'Build the app for a specific IOS device', @@ -189,8 +189,8 @@ export default { 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"', }, { - name: '--configuration ', - description: 'Explicitly set the scheme configuration to use', + name: '--mode ', + description: 'Explicitly set the scheme mode to use', default: 'Debug', }, { diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 89e3991d7..be3e7e439 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -146,12 +146,7 @@ async function runOnSimulator( args, ); - const appPath = getBuildPath( - xcodeProject, - args.configuration, - buildOutput, - scheme, - ); + const appPath = getBuildPath(xcodeProject, args.mode, buildOutput, scheme); logger.info(`Installing "${chalk.bold(appPath)}"`); @@ -218,7 +213,7 @@ async function runOnDevice( if (selectedDevice.type === 'catalyst') { const appPath = getBuildPath( xcodeProject, - args.configuration, + args.mode, buildOutput, scheme, true, @@ -231,7 +226,7 @@ async function runOnDevice( } else { const iosDeployInstallArgs = [ '--bundle', - getBuildPath(xcodeProject, args.configuration, buildOutput, scheme), + getBuildPath(xcodeProject, args.mode, buildOutput, scheme), '--id', selectedDevice.udid, '--justlaunch', @@ -404,7 +399,7 @@ export default { default: 'iPhone 14', }, { - name: '--configuration ', + name: '--mode ', description: 'Explicitly set the scheme configuration to use', default: 'Debug', }, From e976bf556221aba2471b430c41d19325af171a75 Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Wed, 23 Nov 2022 22:37:55 +0100 Subject: [PATCH 09/13] feat: add deprecation note --- .../cli-platform-ios/src/commands/buildIOS/index.ts | 12 +++++++++++- .../cli-platform-ios/src/commands/runIOS/index.ts | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index 8561e2d7e..a6b6672c1 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -20,6 +20,7 @@ import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; import {getDevices} from '../../tools/getDevices'; interface FlagsT extends BuildFlags { + configuration?: string; simulator?: string; device?: string | true; udid?: string; @@ -33,6 +34,10 @@ function buildIOS(_: Array, ctx: Config, args: FlagsT) { ); } + if (args.configuration) { + logger.warn('--configuration has been deprecated. Use --mode instead.'); + } + const {xcodeProject, sourceDir} = ctx.project.ios; process.chdir(sourceDir); @@ -190,9 +195,14 @@ export default { }, { name: '--mode ', - description: 'Explicitly set the scheme mode to use', + description: 'Explicitly set the scheme configuration to use', default: 'Debug', }, + { + name: '--configuration ', + description: + '[Deprecated] Explicitly set the scheme configuration to use', + }, { name: '--scheme ', description: 'Explicitly set Xcode scheme to use', diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index be3e7e439..ba8072e48 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -21,6 +21,7 @@ import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; import {getDevices} from '../../tools/getDevices'; interface FlagsT extends BuildFlags { + configuration?: string; simulator?: string; scheme?: string; projectPath: string; @@ -35,6 +36,10 @@ function runIOS(_: Array, ctx: Config, args: FlagsT) { ); } + if (args.configuration) { + logger.warn('--configuration has been deprecated. Use --mode instead.'); + } + const {xcodeProject, sourceDir} = ctx.project.ios; process.chdir(sourceDir); @@ -403,6 +408,11 @@ export default { description: 'Explicitly set the scheme configuration to use', default: 'Debug', }, + { + name: '--configuration ', + description: + '[Deprecated] Explicitly set the scheme configuration to use', + }, { name: '--scheme ', description: 'Explicitly set Xcode scheme to use', From a1bb039b9dd596fc94c1ea7a41e7fc9fbbf61329 Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Thu, 24 Nov 2022 11:58:26 +0100 Subject: [PATCH 10/13] fix: ts issue --- packages/cli-platform-ios/src/commands/buildIOS/index.ts | 2 +- packages/cli-platform-ios/src/commands/runIOS/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index a6b6672c1..78c778cbf 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -19,7 +19,7 @@ import {BuildFlags, buildProject} from './buildProject'; import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; import {getDevices} from '../../tools/getDevices'; -interface FlagsT extends BuildFlags { +export interface FlagsT extends BuildFlags { configuration?: string; simulator?: string; device?: string | true; diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index ba8072e48..11e74f198 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -20,7 +20,7 @@ import {Device} from '../../types'; import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; import {getDevices} from '../../tools/getDevices'; -interface FlagsT extends BuildFlags { +export interface FlagsT extends BuildFlags { configuration?: string; simulator?: string; scheme?: string; From 2f3f7c0fe4c8fb86de28c5858630495c0e4bb02f Mon Sep 17 00:00:00 2001 From: Mateusz Wit Date: Mon, 28 Nov 2022 14:25:01 +0100 Subject: [PATCH 11/13] docs: reword xcconfig argument for build ios --- docs/commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index 414723ca5..b256f82e8 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -446,7 +446,7 @@ Default: `process.env.RCT_METRO_PORT || 8081` #### `--xcconfig ` -Explicitly pass `xcconfig` options from the command line. +Explicitly set `xcconfig` to use in build. #### `--buildFolder ` From 5a16114ea40d47f978fe4b96c806a04eedcea60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kata?= Date: Tue, 24 Jan 2023 12:33:35 +0100 Subject: [PATCH 12/13] refactor: improvements after code review --- .../cli-platform-ios/src/commands/buildIOS/buildProject.ts | 5 ++--- packages/cli-platform-ios/src/commands/buildIOS/index.ts | 6 +++++- packages/cli-platform-ios/src/commands/runIOS/index.ts | 4 ++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts index c23b0502b..ab70626ce 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/buildProject.ts @@ -5,7 +5,7 @@ import child_process, { import chalk from 'chalk'; import {IOSProjectInfo} from '@react-native-community/cli-types'; import {logger, CLIError} from '@react-native-community/cli-tools'; -import ora from 'ora'; +import {getLoader} from '@react-native-community/cli-tools'; export type BuildFlags = { mode: string; @@ -40,8 +40,7 @@ export function buildProject( ? 'generic/platform=iOS Simulator' : 'generic/platform=iOS', ]; - // @todo use `getLoader` from cli-tools package - const loader = ora(); + const loader = getLoader(); logger.info( `Building ${chalk.dim( `(using "xcodebuild ${xcodebuildArgs.join(' ')}")`, diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index 78c778cbf..b974d6f71 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -36,6 +36,10 @@ function buildIOS(_: Array, ctx: Config, args: FlagsT) { if (args.configuration) { logger.warn('--configuration has been deprecated. Use --mode instead.'); + logger.warn( + 'Parameters were automatically reassigned to --mode on this run.', + ); + args.mode = args.configuration; } const {xcodeProject, sourceDir} = ctx.project.ios; @@ -229,7 +233,7 @@ export default { name: '--terminal ', description: 'Launches the Metro Bundler in a new window using the specified terminal path.', - default: getDefaultUserTerminal, + default: getDefaultUserTerminal(), }, { name: '--xcconfig [string]', diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index 11e74f198..e65ffcbd6 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -38,6 +38,10 @@ function runIOS(_: Array, ctx: Config, args: FlagsT) { if (args.configuration) { logger.warn('--configuration has been deprecated. Use --mode instead.'); + logger.warn( + 'Parameters were automatically reassigned to --mode on this run.', + ); + args.mode = args.configuration; } const {xcodeProject, sourceDir} = ctx.project.ios; From 26e219caa6afb6b8974b23d5d2c9c9ba631c13f4 Mon Sep 17 00:00:00 2001 From: Adam Trzcinski Date: Wed, 25 Jan 2023 18:14:09 +0100 Subject: [PATCH 13/13] fix: pass mode to iOS install command --- docs/commands.md | 4 +- .../src/commands/buildIOS/index.ts | 111 +++++++++--------- .../src/commands/runIOS/index.ts | 66 ++--------- 3 files changed, 66 insertions(+), 115 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index c9cf865ba..d960dbcbe 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -456,7 +456,7 @@ react-native run-ios --simulator "iPhone XS Max" #### `--configuration ` -Explicitly set the scheme configuration to use default: 'Debug'. +[Deprecated] Explicitly set the scheme configuration to use default: 'Debug'. #### `--scheme ` @@ -526,7 +526,7 @@ react-native run-ios --simulator "iPhone XS Max" #### `--configuration ` -Explicitly set the scheme configuration to use default: 'Debug'. +[Deprecated] Explicitly set the scheme configuration to use default: 'Debug'. #### `--scheme ` diff --git a/packages/cli-platform-ios/src/commands/buildIOS/index.ts b/packages/cli-platform-ios/src/commands/buildIOS/index.ts index b974d6f71..257cf1cf6 100644 --- a/packages/cli-platform-ios/src/commands/buildIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/buildIOS/index.ts @@ -172,6 +172,61 @@ function printFoundDevices(devices: Array) { ].join('\n'); } +export const iosBuildOptions = [ + { + name: '--simulator ', + description: + 'Explicitly set simulator to use. Optionally include iOS version between ' + + 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"', + }, + { + name: '--mode ', + description: 'Explicitly set the scheme configuration to use', + default: 'Debug', + }, + { + name: '--configuration ', + description: '[Deprecated] Explicitly set the scheme configuration to use', + }, + { + name: '--scheme ', + description: 'Explicitly set Xcode scheme to use', + }, + { + name: '--device [string]', + description: + 'Explicitly set device to use by name. The value is not required if you have a single device connected.', + }, + { + name: '--udid ', + description: 'Explicitly set device to use by udid', + }, + { + name: '--verbose', + description: 'Do not use xcbeautify or xcpretty even if installed', + }, + { + name: '--port ', + default: process.env.RCT_METRO_PORT || 8081, + parse: Number, + }, + { + name: '--terminal ', + description: + 'Launches the Metro Bundler in a new window using the specified terminal path.', + default: getDefaultUserTerminal(), + }, + { + name: '--xcconfig [string]', + description: 'Explicitly set xcconfig to use', + }, + { + name: '--buildFolder ', + description: + 'Location for iOS build artifacts. Corresponds to Xcode\'s "-derivedDataPath".', + }, +]; + export default { name: 'build-ios', description: 'builds your app on iOS simulator', @@ -190,59 +245,5 @@ export default { cmd: 'react-native build-ios --simulator "IPhone 11"', }, ], - options: [ - { - name: '--simulator ', - description: - 'Explicitly set simulator to use. Optionally include iOS version between ' + - 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"', - }, - { - name: '--mode ', - description: 'Explicitly set the scheme configuration to use', - default: 'Debug', - }, - { - name: '--configuration ', - description: - '[Deprecated] Explicitly set the scheme configuration to use', - }, - { - name: '--scheme ', - description: 'Explicitly set Xcode scheme to use', - }, - { - name: '--device [string]', - description: - 'Explicitly set device to use by name. The value is not required if you have a single device connected.', - }, - { - name: '--udid ', - description: 'Explicitly set device to use by udid', - }, - { - name: '--verbose', - description: 'Do not use xcbeautify or xcpretty even if installed', - }, - { - name: '--port ', - default: process.env.RCT_METRO_PORT || 8081, - parse: Number, - }, - { - name: '--terminal ', - description: - 'Launches the Metro Bundler in a new window using the specified terminal path.', - default: getDefaultUserTerminal(), - }, - { - name: '--xcconfig [string]', - description: 'Explicitly set xcconfig to use', - }, - { - name: '--buildFolder ', - description: - 'Location for iOS build artifacts. Corresponds to Xcode\'s "-derivedDataPath".', - }, - ], + options: iosBuildOptions, }; diff --git a/packages/cli-platform-ios/src/commands/runIOS/index.ts b/packages/cli-platform-ios/src/commands/runIOS/index.ts index d9bcf3608..ffd74b845 100644 --- a/packages/cli-platform-ios/src/commands/runIOS/index.ts +++ b/packages/cli-platform-ios/src/commands/runIOS/index.ts @@ -13,13 +13,10 @@ import chalk from 'chalk'; import {Config, IOSProjectInfo} from '@react-native-community/cli-types'; import {getDestinationSimulator} from '../../tools/getDestinationSimulator'; import {getDevices} from '../../tools/getDevices'; -import { - logger, - CLIError, - getDefaultUserTerminal, -} from '@react-native-community/cli-tools'; +import {logger, CLIError} from '@react-native-community/cli-tools'; import {Device} from '../../types'; import {BuildFlags, buildProject} from '../buildIOS/buildProject'; +import {iosBuildOptions} from '../buildIOS'; export interface FlagsT extends BuildFlags { simulator?: string; @@ -248,7 +245,7 @@ async function runOnSimulator( appPath = getBuildPath( xcodeProject, - args.configuration, + args.mode || args.configuration, buildOutput, scheme, ); @@ -329,7 +326,7 @@ async function runOnDevice( const appPath = getBuildPath( xcodeProject, - args.configuration, + args.mode || args.configuration, buildOutput, scheme, true, @@ -351,7 +348,7 @@ async function runOnDevice( appPath = getBuildPath( xcodeProject, - args.configuration, + args.mode || args.configuration, buildOutput, scheme, ); @@ -412,7 +409,7 @@ function getTargetPaths(buildSettings: string) { function getBuildPath( xcodeProject: IOSProjectInfo, - configuration: string, + mode: BuildFlags['mode'], buildOutput: string, scheme: string, isCatalyst: boolean = false, @@ -427,7 +424,7 @@ function getBuildPath( '-sdk', getPlatformName(buildOutput), '-configuration', - configuration, + mode, '-showBuildSettings', '-json', ], @@ -526,62 +523,15 @@ export default { }, ], options: [ - { - name: '--simulator ', - description: - 'Explicitly set simulator to use. Optionally include iOS version between ' + - 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"', - }, - { - name: '--configuration ', - description: 'Explicitly set the scheme configuration to use', - default: 'Debug', - }, - { - name: '--scheme ', - description: 'Explicitly set Xcode scheme to use', - }, - { - name: '--device [string]', - description: - 'Explicitly set device to use by name. The value is not required if you have a single device connected.', - }, - { - name: '--udid ', - description: 'Explicitly set device to use by udid', - }, + ...iosBuildOptions, { name: '--no-packager', description: 'Do not launch packager while building', }, - { - name: '--verbose', - description: 'Do not use xcbeautify or xcpretty even if installed', - }, - { - name: '--port ', - default: process.env.RCT_METRO_PORT || 8081, - parse: Number, - }, { name: '--binary-path ', description: 'Path relative to project root where pre-built .app binary lives.', }, - { - name: '--terminal ', - description: - 'Launches the Metro Bundler in a new window using the specified terminal path.', - default: getDefaultUserTerminal, - }, - { - name: '--xcconfig [string]', - description: 'Explicitly set xcconfig to use', - }, - { - name: '--buildFolder ', - description: - 'Location for iOS build artifacts. Corresponds to Xcode\'s "-derivedDataPath".', - }, ], };