"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; function _child_process() { const data = _interopRequireDefault(require("child_process")); _child_process = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _fs() { const data = _interopRequireDefault(require("fs")); _fs = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } var _getDestinationSimulator = require("../../tools/getDestinationSimulator"); function _cliTools() { const data = require("@react-native-community/cli-tools"); _cliTools = function () { return data; }; return data; } var _buildProject = require("../buildIOS/buildProject"); var _buildIOS = require("../buildIOS"); var _listIOSDevices = _interopRequireDefault(require("../../tools/listIOSDevices")); var _checkIfConfigurationExists = require("../../tools/checkIfConfigurationExists"); var _getProjectInfo = require("../../tools/getProjectInfo"); var _getConfigurationScheme = require("../../tools/getConfigurationScheme"); var _selectFromInteractiveMode = require("../../tools/selectFromInteractiveMode"); var _prompts = require("../../tools/prompts"); var _getSimulators = _interopRequireDefault(require("../../tools/getSimulators")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * 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. * */ async function runIOS(_, ctx, args) { _cliTools().link.setPlatform('ios'); if (ctx.reactNativeVersion !== 'unknown') { _cliTools().link.setVersion(ctx.reactNativeVersion); } if (!ctx.project.ios) { throw new (_cliTools().CLIError)('iOS project folder not found. Are you sure this is a React Native project?'); } const { xcodeProject, sourceDir } = ctx.project.ios; if (!xcodeProject) { throw new (_cliTools().CLIError)(`Could not find Xcode project files in "${sourceDir}" folder`); } process.chdir(sourceDir); if (args.binaryPath) { args.binaryPath = _path().default.isAbsolute(args.binaryPath) ? args.binaryPath : _path().default.join(ctx.root, args.binaryPath); if (!_fs().default.existsSync(args.binaryPath)) { throw new (_cliTools().CLIError)('binary-path was specified, but the file was not found.'); } } if (args.configuration) { _cliTools().logger.warn('--configuration has been deprecated. Use --mode instead.'); _cliTools().logger.warn('Parameters were automatically reassigned to --mode on this run.'); args.mode = args.configuration; } const projectInfo = (0, _getProjectInfo.getProjectInfo)(); if (args.mode) { (0, _checkIfConfigurationExists.checkIfConfigurationExists)(projectInfo, args.mode); } const inferredSchemeName = _path().default.basename(xcodeProject.name, _path().default.extname(xcodeProject.name)); let scheme = args.scheme || inferredSchemeName; let mode = args.mode; if (args.interactive) { const selection = await (0, _selectFromInteractiveMode.selectFromInteractiveMode)({ scheme, mode }); if (selection.scheme) { scheme = selection.scheme; } if (selection.mode) { mode = selection.mode; } } const modifiedArgs = { ...args, scheme, mode }; modifiedArgs.mode = (0, _getConfigurationScheme.getConfigurationScheme)({ scheme: modifiedArgs.scheme, mode: modifiedArgs.mode }, sourceDir); _cliTools().logger.info(`Found Xcode ${xcodeProject.isWorkspace ? 'workspace' : 'project'} "${_chalk().default.bold(xcodeProject.name)}"`); const availableDevices = await (0, _listIOSDevices.default)(); if (modifiedArgs.listDevices || modifiedArgs.interactive) { if (modifiedArgs.device || modifiedArgs.udid) { _cliTools().logger.warn(`Both ${modifiedArgs.device ? 'device' : 'udid'} and "list-devices" parameters were passed to "run" command. We will list available devices and let you choose from one.`); } const selectedDevice = await (0, _prompts.promptForDeviceSelection)(availableDevices); if (!selectedDevice) { throw new (_cliTools().CLIError)(`Failed to select device, please try to run app without ${args.listDevices ? 'list-devices' : 'interactive'} command.`); } if (selectedDevice.type === 'simulator') { return runOnSimulator(xcodeProject, scheme, modifiedArgs, selectedDevice); } else { return runOnDevice(selectedDevice, scheme, xcodeProject, modifiedArgs); } } if (!modifiedArgs.device && !modifiedArgs.udid && !modifiedArgs.simulator) { const bootedDevices = availableDevices.filter(({ type, isAvailable }) => type === 'device' && isAvailable); const simulators = (0, _getSimulators.default)(); const bootedSimulators = Object.keys(simulators.devices).map(key => simulators.devices[key]).reduce((acc, val) => acc.concat(val), []).filter(({ state }) => state === 'Booted'); const booted = [...bootedDevices, ...bootedSimulators]; if (booted.length === 0) { _cliTools().logger.info('No booted devices or simulators found. Launching first available simulator...'); return runOnSimulator(xcodeProject, scheme, modifiedArgs); } _cliTools().logger.info(`Found booted ${booted.map(({ name }) => name).join(', ')}`); return runOnBootedDevicesSimulators(scheme, xcodeProject, modifiedArgs, bootedDevices, bootedSimulators); } if (modifiedArgs.device && modifiedArgs.udid) { return _cliTools().logger.error('The `device` and `udid` options are mutually exclusive.'); } if (modifiedArgs.udid) { const device = availableDevices.find(d => d.udid === modifiedArgs.udid); if (!device) { return _cliTools().logger.error(`Could not find a device with udid: "${_chalk().default.bold(modifiedArgs.udid)}". ${printFoundDevices(availableDevices)}`); } if (device.type === 'simulator') { return runOnSimulator(xcodeProject, scheme, modifiedArgs); } else { return runOnDevice(device, scheme, xcodeProject, modifiedArgs); } } else if (modifiedArgs.device) { const physicalDevices = availableDevices.filter(({ type }) => type !== 'simulator'); const device = matchingDevice(physicalDevices, modifiedArgs.device); if (device) { return runOnDevice(device, scheme, xcodeProject, modifiedArgs); } } else { runOnSimulator(xcodeProject, scheme, modifiedArgs); } } async function runOnBootedDevicesSimulators(scheme, xcodeProject, args, devices, simulators) { for (const device of devices) { await runOnDevice(device, scheme, xcodeProject, args); } for (const simulator of simulators) { await runOnSimulator(xcodeProject, scheme, args, simulator); } } async function runOnSimulator(xcodeProject, scheme, args, simulator) { // let selectedSimulator; /** * If provided simulator does not exist, try simulators in following order * - iPhone 14 * - iPhone 13 * - iPhone 12 * - iPhone 11 */ let selectedSimulator; if (simulator) { selectedSimulator = simulator; } else { const fallbackSimulators = ['iPhone 14', 'iPhone 13', 'iPhone 12', 'iPhone 11']; selectedSimulator = (0, _getDestinationSimulator.getDestinationSimulator)(args, fallbackSimulators); } if (!selectedSimulator) { throw new (_cliTools().CLIError)(`No simulator available with ${args.simulator ? `name "${args.simulator}"` : `udid "${args.udid}"`}`); } /** * Booting simulator through `xcrun simctl boot` will boot it in the `headless` mode * (running in the background). * * In order for user to see the app and the simulator itself, we have to make sure * that the Simulator.app is running. * * We also pass it `-CurrentDeviceUDID` so that when we launch it for the first time, * it will not boot the "default" device, but the one we set. If the app is already running, * this flag has no effect. */ const activeDeveloperDir = _child_process().default.execFileSync('xcode-select', ['-p'], { encoding: 'utf8' }).trim(); _child_process().default.execFileSync('open', [`${activeDeveloperDir}/Applications/Simulator.app`, '--args', '-CurrentDeviceUDID', selectedSimulator.udid]); if (!selectedSimulator.booted) { bootSimulator(selectedSimulator); } let buildOutput, appPath; if (!args.binaryPath) { buildOutput = await (0, _buildProject.buildProject)(xcodeProject, selectedSimulator.udid, scheme, args); appPath = getBuildPath(xcodeProject, args.mode || args.configuration, buildOutput, scheme); } else { appPath = args.binaryPath; } _cliTools().logger.info(`Installing "${_chalk().default.bold(appPath)} on ${selectedSimulator.name}"`); _child_process().default.spawnSync('xcrun', ['simctl', 'install', selectedSimulator.udid, appPath], { stdio: 'inherit' }); const bundleID = _child_process().default.execFileSync('/usr/libexec/PlistBuddy', ['-c', 'Print:CFBundleIdentifier', _path().default.join(appPath, 'Info.plist')], { encoding: 'utf8' }).trim(); _cliTools().logger.info(`Launching "${_chalk().default.bold(bundleID)}"`); const result = _child_process().default.spawnSync('xcrun', ['simctl', 'launch', selectedSimulator.udid, bundleID]); if (result.status === 0) { _cliTools().logger.success('Successfully launched the app on the simulator'); } else { _cliTools().logger.error('Failed to launch the app on simulator', result.stderr.toString()); } } async function runOnDevice(selectedDevice, scheme, xcodeProject, args) { if (args.binaryPath && selectedDevice.type === 'catalyst') { throw new (_cliTools().CLIError)('binary-path was specified for catalyst device, which is not supported.'); } const isIOSDeployInstalled = _child_process().default.spawnSync('ios-deploy', ['--version'], { encoding: 'utf8' }); if (isIOSDeployInstalled.error) { throw new (_cliTools().CLIError)(`Failed to install the app on the device because we couldn't execute the "ios-deploy" command. Please install it by running "${_chalk().default.bold('brew install ios-deploy')}" and try again.`); } if (selectedDevice.type === 'catalyst') { const buildOutput = await (0, _buildProject.buildProject)(xcodeProject, selectedDevice.udid, scheme, args); const appPath = getBuildPath(xcodeProject, args.mode || args.configuration, buildOutput, scheme, true); const appProcess = _child_process().default.spawn(`${appPath}/${scheme}`, [], { detached: true, stdio: 'ignore' }); appProcess.unref(); } else { let buildOutput, appPath; if (!args.binaryPath) { buildOutput = await (0, _buildProject.buildProject)(xcodeProject, selectedDevice.udid, scheme, args); appPath = getBuildPath(xcodeProject, args.mode || args.configuration, buildOutput, scheme); } else { appPath = args.binaryPath; } const iosDeployInstallArgs = ['--bundle', appPath, '--id', selectedDevice.udid, '--justlaunch']; _cliTools().logger.info(`Installing and launching your app on ${selectedDevice.name}`); const iosDeployOutput = _child_process().default.spawnSync('ios-deploy', iosDeployInstallArgs, { encoding: 'utf8' }); if (iosDeployOutput.error) { throw new (_cliTools().CLIError)(`Failed to install the app on the device. We've encountered an error in "ios-deploy" command: ${iosDeployOutput.error.message}`); } } return _cliTools().logger.success('Installed the app on the device.'); } function bootSimulator(selectedSimulator) { const simulatorFullName = formattedDeviceName(selectedSimulator); _cliTools().logger.info(`Launching ${simulatorFullName}`); _child_process().default.spawnSync('xcrun', ['simctl', 'boot', selectedSimulator.udid]); } function getTargetPaths(buildSettings) { const settings = JSON.parse(buildSettings); // Find app in all building settings - look for WRAPPER_EXTENSION: 'app', for (const i in settings) { const wrapperExtension = settings[i].buildSettings.WRAPPER_EXTENSION; if (wrapperExtension === 'app') { return { targetBuildDir: settings[i].buildSettings.TARGET_BUILD_DIR, executableFolderPath: settings[i].buildSettings.EXECUTABLE_FOLDER_PATH }; } } return {}; } function getBuildPath(xcodeProject, mode, buildOutput, scheme, isCatalyst = false) { const buildSettings = _child_process().default.execFileSync('xcodebuild', [xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, '-scheme', scheme, '-sdk', getPlatformName(buildOutput), '-configuration', mode, '-showBuildSettings', '-json'], { encoding: 'utf8' }); const { targetBuildDir, executableFolderPath } = getTargetPaths(buildSettings); if (!targetBuildDir) { throw new (_cliTools().CLIError)('Failed to get the target build directory.'); } if (!executableFolderPath) { throw new (_cliTools().CLIError)('Failed to get the app name.'); } return `${targetBuildDir}${isCatalyst ? '-maccatalyst' : ''}/${executableFolderPath}`; } function getPlatformName(buildOutput) { // Xcode can sometimes escape `=` with a backslash or put the value in quotes const platformNameMatch = /export PLATFORM_NAME\\?="?(\w+)"?$/m.exec(buildOutput); if (!platformNameMatch) { throw new (_cliTools().CLIError)('Couldn\'t find "PLATFORM_NAME" variable in xcodebuild output. Please report this issue and run your project with Xcode instead.'); } return platformNameMatch[1]; } function matchingDevice(devices, deviceName) { if (deviceName === true) { const firstIOSDevice = devices.find(d => d.type === 'device'); if (firstIOSDevice) { _cliTools().logger.info(`Using first available device named "${_chalk().default.bold(firstIOSDevice.name)}" due to lack of name supplied.`); return firstIOSDevice; } else { _cliTools().logger.error('No iOS devices connected.'); return undefined; } } const deviceByName = devices.find(device => device.name === deviceName || formattedDeviceName(device) === deviceName); if (!deviceByName) { _cliTools().logger.error(`Could not find a device named: "${_chalk().default.bold(String(deviceName))}". ${printFoundDevices(devices)}`); } return deviceByName; } function formattedDeviceName(simulator) { return simulator.version ? `${simulator.name} (${simulator.version})` : simulator.name; } function printFoundDevices(devices) { return ['Available devices:', ...devices.map(device => ` - ${device.name} (${device.udid})`)].join('\n'); } var _default = { name: 'run-ios', description: 'builds your app and starts it on iOS simulator', func: runIOS, 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: [..._buildIOS.iosBuildOptions, { name: '--no-packager', description: 'Do not launch packager while building' }, { name: '--binary-path ', description: 'Path relative to project root where pre-built .app binary lives.' }, { name: '--list-devices', description: 'List all available iOS devices and simulators and let you choose one to run the app. ' }, { name: '--interactive', description: 'Explicitly select which scheme and configuration to use before running a build and select device to run the application.' }] }; exports.default = _default; //# sourceMappingURL=index.ts.map