"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addMediaAsync = addMediaAsync; exports.bootAsync = bootAsync; exports.captureScreenAsync = captureScreenAsync; exports.deleteUnavailableAsync = deleteUnavailableAsync; exports.eraseAllAsync = eraseAllAsync; exports.eraseAsync = eraseAsync; exports.getContainerPathAsync = getContainerPathAsync; exports.getDefaultSimulatorDeviceUDIDAsync = getDefaultSimulatorDeviceUDIDAsync; exports.installAsync = installAsync; exports.isLicenseOutOfDate = isLicenseOutOfDate; exports.isXcrunInstalledAsync = isXcrunInstalledAsync; exports.listAsync = listAsync; exports.listDevicesAsync = listDevicesAsync; exports.listSimulatorDevicesAsync = listSimulatorDevicesAsync; exports.openBundleIdAsync = openBundleIdAsync; exports.openURLAsync = openURLAsync; exports.parseXcrunError = parseXcrunError; exports.runBootAsync = runBootAsync; exports.setAppearanceAsync = setAppearanceAsync; exports.shutdownAsync = shutdownAsync; exports.simctlAsync = simctlAsync; exports.uninstallAsync = uninstallAsync; exports.updatePermissionsAsync = updatePermissionsAsync; exports.waitForDeviceToBootAsync = waitForDeviceToBootAsync; exports.xcrunAsync = xcrunAsync; exports.xcrunWithLogging = xcrunWithLogging; function _spawnAsync() { const data = _interopRequireDefault(require("@expo/spawn-async")); _spawnAsync = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _child_process() { const data = require("child_process"); _child_process = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _waitForActionAsync() { const data = require("./apple/utils/waitForActionAsync"); _waitForActionAsync = function () { return data; }; return data; } function _internal() { const data = require("./internal"); _internal = function () { return data; }; return data; } function _profileMethod() { const data = require("./utils/profileMethod"); _profileMethod = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } async function getDefaultSimulatorDeviceUDIDAsync() { try { const { stdout: defaultDeviceUDID } = await (0, _spawnAsync().default)('defaults', ['read', 'com.apple.iphonesimulator', 'CurrentDeviceUDID']); return defaultDeviceUDID.trim(); } catch { return null; } } /** * Returns the local path for the installed tar.app. Returns null when the app isn't installed. * * @param props.udid device udid. * @param props.bundleIdentifier bundle identifier for app * @returns local file path to installed app binary, e.g. '/Users/evanbacon/Library/Developer/CoreSimulator/Devices/EFEEA6EF-E3F5-4EDE-9B72-29EAFA7514AE/data/Containers/Bundle/Application/FA43A0C6-C2AD-442D-B8B1-EAF3E88CF3BF/Exponent-2.21.3.tar.app' */ async function getContainerPathAsync({ udid, bundleIdentifier }) { if (_internal().CoreSimulator.isEnabled()) { return _internal().CoreSimulator.getContainerPathAsync({ udid, bundleIdentifier }); } try { const { stdout } = await xcrunAsync(['simctl', 'get_app_container', deviceUDIDOrBooted(udid), bundleIdentifier]); return stdout.trim(); } catch (error) { var _error$stderr; if ((_error$stderr = error.stderr) !== null && _error$stderr !== void 0 && _error$stderr.match(/No such file or directory/)) { return null; } throw error; } } async function waitForDeviceToBootAsync({ udid }) { return (0, _waitForActionAsync().waitForActionAsync)({ action: () => bootAsync({ udid }) }); } async function openURLAsync(options) { try { // Skip logging since this is likely to fail. await xcrunAsync(['simctl', 'openurl', deviceUDIDOrBooted(options.udid), options.url]); } catch (error) { var _error$stderr2; if (!((_error$stderr2 = error.stderr) !== null && _error$stderr2 !== void 0 && _error$stderr2.match(/Unable to lookup in current state: Shut/))) { throw error; } // If the device was in a weird in-between state ("Shutting Down" or "Shutdown"), then attempt to reboot it and try again. // This can happen when quitting the Simulator app, and immediately pressing `i` to reopen the project. // First boot the simulator await runBootAsync({ udid: deviceUDIDOrBooted(options.udid) }); // Finally, try again... return await openURLAsync(options); } } async function openBundleIdAsync(options) { return xcrunAsync(['simctl', 'launch', deviceUDIDOrBooted(options.udid), options.bundleIdentifier]); } // This will only boot in headless mode if the Simulator app is not running. async function bootAsync({ udid }) { if (_internal().CoreSimulator.isEnabled()) { const device = await _internal().CoreSimulator.getDeviceInfoAsync({ udid }).catch(() => null); if ((device === null || device === void 0 ? void 0 : device.state) === 'Booted') { return device; } await runBootAsync({ udid }); return await (0, _profileMethod().profileMethod)(_internal().CoreSimulator.getDeviceInfoAsync)({ udid }); } // TODO: Deprecate await runBootAsync({ udid }); return await isSimulatorBootedAsync({ udid }); } async function getBootedSimulatorsAsync() { const simulatorDeviceInfo = await listAsync('devices'); return Object.values(simulatorDeviceInfo.devices).reduce((prev, runtime) => { return prev.concat(runtime.filter(device => device.state === 'Booted')); }, []); } async function isSimulatorBootedAsync({ udid }) { // Simulators can be booted even if the app isn't running :( const devices = await getBootedSimulatorsAsync(); if (udid) { var _devices$find; return (_devices$find = devices.find(bootedDevice => bootedDevice.udid === udid)) !== null && _devices$find !== void 0 ? _devices$find : null; } else { var _devices$; return (_devices$ = devices[0]) !== null && _devices$ !== void 0 ? _devices$ : null; } } async function runBootAsync({ udid }) { try { // Skip logging since this is likely to fail. await xcrunAsync(['simctl', 'boot', udid]); } catch (error) { var _error$stderr3; if (!((_error$stderr3 = error.stderr) !== null && _error$stderr3 !== void 0 && _error$stderr3.match(/Unable to boot device in current state: Booted/))) { throw error; } } } async function installAsync(options) { return simctlAsync(['install', deviceUDIDOrBooted(options.udid), options.dir]); } async function uninstallAsync(options) { return simctlAsync(['uninstall', deviceUDIDOrBooted(options.udid), options.bundleIdentifier]); } function parseSimControlJSONResults(input) { try { return JSON.parse(input); } catch (error) { // Nov 15, 2020: Observed this can happen when opening the simulator and the simulator prompts the user to update the XC command line tools. // Unexpected token I in JSON at position 0 if (error.message.match('Unexpected token')) { _internal().Logger.global.error(`Apple's simctl returned malformed JSON:\n${input}`); } throw error; } } // TODO: Compare with // const results = await SimControl.xcrunAsync(['instruments', '-s']); async function listAsync(type, query) { const result = await simctlAsync(['list', type, '--json', query]); const info = parseSimControlJSONResults(result.stdout); for (const runtime of Object.keys(info.devices)) { // Given a string like 'com.apple.CoreSimulator.SimRuntime.tvOS-13-4' const runtimeSuffix = runtime.split('com.apple.CoreSimulator.SimRuntime.').pop(); // Create an array [tvOS, 13, 4] const [osType, ...osVersionComponents] = runtimeSuffix.split('-'); // Join the end components [13, 4] -> '13.4' const osVersion = osVersionComponents.join('.'); const sims = info.devices[runtime]; for (const device of sims) { device.runtime = runtime; device.osVersion = osVersion; device.windowName = `${device.name} (${osVersion})`; device.osType = osType; } } return info; } async function listSimulatorDevicesAsync() { if (_internal().CoreSimulator.isEnabled()) { return _internal().CoreSimulator.listDevicesAsync(); } const simulatorDeviceInfo = await listAsync('devices'); return Object.values(simulatorDeviceInfo.devices).reduce((prev, runtime) => { return prev.concat(runtime); }, []); } /** * Get a list of all connected devices. */ async function listDevicesAsync() { if (_internal().AppleDevice.isEnabled()) { const results = await _internal().AppleDevice.getConnectedDevices(); // TODO: Add support for osType (ipad, watchos, etc) return results.map(device => { var _ref, _device$DeviceName; return { // TODO: Better name name: (_ref = (_device$DeviceName = device.DeviceName) !== null && _device$DeviceName !== void 0 ? _device$DeviceName : device.ProductType) !== null && _ref !== void 0 ? _ref : 'unknown ios device', model: device.ProductType, osVersion: device.ProductVersion, deviceType: 'device', udid: device.UniqueDeviceID }; }); } const { output } = await xcrunAsync(['xctrace', 'list', 'devices']); const text = output.join(''); const devices = []; if (!text.includes('== Simulators ==')) { return []; } const lines = text.split('\n'); for (const line of lines) { if (line === '== Simulators ==') { break; } const device = line.match(/(.*?) (\(([0-9.]+)\) )?\(([0-9A-F-]+)\)/i); if (device) { const [, name,, osVersion, udid] = device; const metadata = { name, udid, osVersion: osVersion !== null && osVersion !== void 0 ? osVersion : '??', deviceType: osVersion ? 'device' : 'catalyst' }; devices.push(metadata); } } return devices; } async function shutdownAsync(udid) { try { return simctlAsync(['shutdown', deviceUDIDOrBooted(udid)]); } catch (e) { var _e$message; if (!((_e$message = e.message) !== null && _e$message !== void 0 && _e$message.includes('No devices are booted.'))) { throw e; } } return null; } // Some permission changes will terminate the application if running async function updatePermissionsAsync(udid, action, permission, bundleIdentifier) { return simctlAsync(['privacy', deviceUDIDOrBooted(udid), action, permission, bundleIdentifier]); } async function setAppearanceAsync(udid, theme) { return simctlAsync(['ui', deviceUDIDOrBooted(udid), theme]); } // Cannot be invoked unless the simulator is `shutdown` async function eraseAsync(udid) { return simctlAsync(['erase', deviceUDIDOrBooted(udid)]); } async function eraseAllAsync() { return simctlAsync(['erase', 'all']); } // Add photos and videos to the simulator's gallery async function addMediaAsync(udid, mediaPath) { return simctlAsync(['addmedia', deviceUDIDOrBooted(udid), mediaPath]); } async function captureScreenAsync(udid, captureType, outputFilePath) { return simctlAsync(['io', deviceUDIDOrBooted(udid), captureType, `—type=${_path().default.extname(outputFilePath)}`, outputFilePath]); } // Clear all unused simulators async function deleteUnavailableAsync() { return simctlAsync(['delete', 'unavailable']); } async function simctlAsync([command, ...args], options) { return xcrunWithLogging( // @ts-ignore ['simctl', command, ...args.filter(Boolean)], options); } function deviceUDIDOrBooted(udid) { return udid ? udid : 'booted'; } function isLicenseOutOfDate(text) { if (!text) { return false; } const lower = text.toLowerCase(); return lower.includes('xcode') && lower.includes('license'); } async function isXcrunInstalledAsync() { try { (0, _child_process().execSync)('xcrun --version', { stdio: 'ignore' }); return true; } catch { return false; } } async function xcrunAsync(args, options) { _internal().Logger.global.debug('Running: xcrun ' + args.join(' ')); try { return await (0, _spawnAsync().default)('xcrun', args, options); } catch (e) { throw parseXcrunError(e); } } function parseXcrunError(e) { var _e$stderr; if (isLicenseOutOfDate(e.stdout) || isLicenseOutOfDate(e.stderr)) { return new (_internal().XDLError)('XCODE_LICENSE_NOT_ACCEPTED', 'Xcode license is not accepted. Please run `sudo xcodebuild -license`.'); } else if ((_e$stderr = e.stderr) !== null && _e$stderr !== void 0 && _e$stderr.includes('not a developer tool or in PATH')) { return new (_internal().XDLError)('SIMCTL_NOT_AVAILABLE', `You may need to run ${_chalk().default.bold('sudo xcode-select -s /Applications/Xcode.app')} and try again.`); } // Attempt to craft a better error message... if (Array.isArray(e.output)) { e.message += '\n' + e.output.join('\n').trim(); } else if (e.stderr) { e.message += '\n' + e.stderr; } return e; } async function xcrunWithLogging(args, options) { try { return await xcrunAsync(args, options); } catch (e) { _internal().Logger.global.error(`Error running \`xcrun ${args.join(' ')}\`: ${e.stderr || e.message}`); throw e; } } //# sourceMappingURL=SimControl.js.map