You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1082 lines
37 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports._downloadSimulatorAppAsync = _downloadSimulatorAppAsync;
exports.activateSimulatorWindowAsync = activateSimulatorWindowAsync;
exports.closeSimulatorAppAsync = closeSimulatorAppAsync;
exports.doesExpoClientNeedUpdatedAsync = doesExpoClientNeedUpdatedAsync;
Object.defineProperty(exports, "ensureSimulatorAppRunningAsync", {
enumerable: true,
get: function () {
return _ensureSimulatorAppRunningAsync().ensureSimulatorAppRunningAsync;
}
});
exports.ensureSimulatorOpenAsync = ensureSimulatorOpenAsync;
exports.ensureXcodeCommandLineToolsInstalledAsync = ensureXcodeCommandLineToolsInstalledAsync;
exports.ensureXcodeInstalledAsync = ensureXcodeInstalledAsync;
exports.expoVersionOnSimulatorAsync = expoVersionOnSimulatorAsync;
exports.installExpoOnSimulatorAsync = installExpoOnSimulatorAsync;
exports.isExpoClientInstalledOnSimulatorAsync = isExpoClientInstalledOnSimulatorAsync;
exports.isPlatformSupported = isPlatformSupported;
exports.isSimulatorBootedAsync = isSimulatorBootedAsync;
exports.isSimulatorInstalledAsync = isSimulatorInstalledAsync;
exports.openProjectAsync = openProjectAsync;
exports.openWebProjectAsync = openWebProjectAsync;
exports.promptForSimulatorAsync = promptForSimulatorAsync;
exports.resolveApplicationIdAsync = resolveApplicationIdAsync;
exports.sortDefaultDeviceToBeginningAsync = sortDefaultDeviceToBeginningAsync;
exports.streamLogsAsync = streamLogsAsync;
exports.uninstallExpoAppFromSimulatorAsync = uninstallExpoAppFromSimulatorAsync;
exports.upgradeExpoAsync = upgradeExpoAsync;
exports.waitForExpoClientInstalledOnSimulatorAsync = waitForExpoClientInstalledOnSimulatorAsync;
exports.waitForExpoClientUninstalledOnSimulatorAsync = waitForExpoClientUninstalledOnSimulatorAsync;
function _config() {
const data = require("@expo/config");
_config = function () {
return data;
};
return data;
}
function _configPlugins() {
const data = require("@expo/config-plugins");
_configPlugins = function () {
return data;
};
return data;
}
function osascript() {
const data = _interopRequireWildcard(require("@expo/osascript"));
osascript = function () {
return data;
};
return data;
}
function _plist() {
const data = _interopRequireDefault(require("@expo/plist"));
_plist = function () {
return data;
};
return data;
}
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 _fsExtra() {
const data = _interopRequireDefault(require("fs-extra"));
_fsExtra = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _prompts() {
const data = _interopRequireDefault(require("prompts"));
_prompts = function () {
return data;
};
return data;
}
function _semver() {
const data = _interopRequireDefault(require("semver"));
_semver = function () {
return data;
};
return data;
}
function _ensureSimulatorAppRunningAsync() {
const data = require("./apple/utils/ensureSimulatorAppRunningAsync");
_ensureSimulatorAppRunningAsync = 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 }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
let _lastUrl = null;
const EXPO_GO_BUNDLE_IDENTIFIER = 'host.exp.Exponent';
const SUGGESTED_XCODE_VERSION = `${_internal().Xcode.minimumVersion}.0`;
const INSTALL_WARNING_TIMEOUT = 60 * 1000;
function isPlatformSupported() {
return process.platform === 'darwin';
}
/**
* Ensure Xcode is installed an recent enough to be used with Expo.
*
* @return true when Xcode is installed, false when the process should end.
*/
async function ensureXcodeInstalledAsync() {
const promptToOpenAppStoreAsync = async message => {
// This prompt serves no purpose accept informing the user what to do next, we could just open the App Store but it could be confusing if they don't know what's going on.
const confirm = await _internal().Prompts.confirmAsync({
initial: true,
message
});
if (confirm) {
_internal().Logger.global.info(`Going to the App Store, re-run Expo when Xcode is finished installing.`);
_internal().Xcode.openAppStore(_internal().Xcode.appStoreId);
}
};
const version = (0, _profileMethod().profileMethod)(_internal().Xcode.getXcodeVersion)();
if (!version) {
// Almost certainly Xcode isn't installed.
await promptToOpenAppStoreAsync(`Xcode needs to be installed (don't worry, you won't have to use it), would you like to continue to the App Store?`);
return false;
}
if (!_semver().default.valid(version)) {
// Not sure why this would happen, if it does we should add a more confident error message.
_internal().Logger.global.error(`Xcode version is in an unknown format: ${version}`);
return false;
}
if (_semver().default.lt(version, SUGGESTED_XCODE_VERSION)) {
// Xcode version is too old.
await promptToOpenAppStoreAsync(`Xcode (${version}) needs to be updated to at least version ${_internal().Xcode.minimumVersion}, would you like to continue to the App Store?`);
return false;
}
return true;
}
let _isXcodeCLIInstalled = null;
async function ensureXcodeCommandLineToolsInstalledAsync() {
// NOTE(Bacon): See `isSimulatorInstalledAsync` for more info on why we cache this value.
if (_isXcodeCLIInstalled != null) {
return _isXcodeCLIInstalled;
}
const _ensureXcodeCommandLineToolsInstalledAsync = async () => {
if (!(await ensureXcodeInstalledAsync())) {
// Need Xcode to install the CLI afaict
return false;
} else if (await _internal().SimControl.isXcrunInstalledAsync()) {
// Run this second to ensure the Xcode version check is run.
return true;
}
async function pendingAsync() {
if (await _internal().SimControl.isXcrunInstalledAsync()) {
return true;
} else {
await (0, _internal().delayAsync)(100);
return await pendingAsync();
}
}
// This prompt serves no purpose accept informing the user what to do next, we could just open the App Store but it could be confusing if they don't know what's going on.
const confirm = await _internal().Prompts.confirmAsync({
initial: true,
message: `Xcode ${_chalk().default.bold`Command Line Tools`} needs to be installed (requires ${_chalk().default.bold`sudo`}), continue?`
});
if (!confirm) {
return false;
}
try {
await (0, _spawnAsync().default)('sudo', ['xcode-select', '--install'
// TODO: Is there any harm in skipping this?
// '--switch', '/Applications/Xcode.app'
]);
// Most likely the user will cancel the process, but if they don't this will continue checking until the CLI is available.
await pendingAsync();
return true;
} catch {
// TODO: Figure out why this might get called (cancel early, network issues, server problems)
// TODO: Handle me
}
return false;
};
_isXcodeCLIInstalled = await _ensureXcodeCommandLineToolsInstalledAsync();
return _isXcodeCLIInstalled;
}
async function getSimulatorAppIdAsync() {
let result;
try {
result = (await osascript().execAsync('id of app "Simulator"')).trim();
} catch {
// This error may occur in CI where the users intends to install just the simulators but no Xcode.
return null;
}
return result;
}
let _isSimulatorInstalled = null;
// Simulator installed
async function isSimulatorInstalledAsync() {
if (_isSimulatorInstalled != null) {
return _isSimulatorInstalled;
}
// NOTE(Bacon): This method can take upwards of 1-2s to run so we should cache the results per process.
// If the user installs Xcode while expo start is running, they'll need to restart
// the process for the command to work properly.
// This is better than waiting 1-2s every time you want to open the app on iOS.
const _isSimulatorInstalledAsync = async () => {
// Check to ensure Xcode and its CLI are installed and up to date.
if (!(await ensureXcodeCommandLineToolsInstalledAsync())) {
return false;
}
// TODO: extract into ensureSimulatorInstalled method
const result = await getSimulatorAppIdAsync();
if (!result) {
// This error may occur in CI where the users intends to install just the simulators but no Xcode.
_internal().Logger.global.error("Can't determine id of Simulator app; the Simulator is most likely not installed on this machine. Run `sudo xcode-select -s /Applications/Xcode.app`");
return false;
}
if (result !== 'com.apple.iphonesimulator' && result !== 'com.apple.CoreSimulator.SimulatorTrampoline') {
// TODO: FYI
_internal().Logger.global.warn("Simulator is installed but is identified as '" + result + "'; don't know what that is.");
return false;
}
// make sure we can run simctl
try {
await _internal().SimControl.simctlAsync(['help']);
} catch (e) {
if (e.isXDLError) {
_internal().Logger.global.error(e.toString());
} else {
_internal().Logger.global.warn(`Unable to run simctl: ${e.toString()}`);
_internal().Logger.global.error('xcrun may not be configured correctly. Try running `sudo xcode-select --reset` and running this again.');
}
return false;
}
return true;
};
_isSimulatorInstalled = await _isSimulatorInstalledAsync();
return _isSimulatorInstalled;
}
/**
* Ensure a simulator is booted and the Simulator app is opened.
* This is where any timeout related error handling should live.
*/
async function ensureSimulatorOpenAsync({
udid,
osType
} = {}, tryAgain = true) {
// Use a default simulator if none was specified
if (!udid) {
// If a simulator is open, side step the entire booting sequence.
const simulatorOpenedByApp = await getBestBootedSimulatorAsync({
osType
});
if (simulatorOpenedByApp) {
return simulatorOpenedByApp;
}
// Otherwise, find the best possible simulator from user defaults and continue
udid = await getBestUnbootedSimulatorAsync({
osType
});
}
const bootedDevice = await (0, _profileMethod().profileMethod)(_internal().SimControl.waitForDeviceToBootAsync)({
udid
});
if (!bootedDevice) {
// Give it a second chance, this might not be needed but it could potentially lead to a better UX on slower devices.
if (tryAgain) {
return await ensureSimulatorOpenAsync({
udid,
osType
}, false);
}
// TODO: We should eliminate all needs for a timeout error, it's bad UX to get an error about the simulator not starting while the user can clearly see it starting on their slow computer.
throw new (_waitForActionAsync().TimeoutError)(`Simulator didn't boot fast enough. Try opening Simulator first, then running your app.`);
}
return bootedDevice;
}
async function getBestBootedSimulatorAsync({
osType
}) {
var _simulatorOpenedByApp;
let simulatorOpenedByApp;
if (_internal().CoreSimulator.isEnabled()) {
simulatorOpenedByApp = await _internal().CoreSimulator.getDeviceInfoAsync().catch(() => null);
} else {
const simulatorDeviceInfo = await _internal().SimControl.listAsync('devices');
const devices = Object.values(simulatorDeviceInfo.devices).reduce((prev, runtime) => {
return prev.concat(runtime.filter(device => device.state === 'Booted'));
}, []);
simulatorOpenedByApp = devices[0];
}
// This should prevent opening a second simulator in the chance that default
// simulator doesn't match what the Simulator app would open by default.
if ((_simulatorOpenedByApp = simulatorOpenedByApp) !== null && _simulatorOpenedByApp !== void 0 && _simulatorOpenedByApp.udid && (!osType || osType && simulatorOpenedByApp.osType === osType)) {
return simulatorOpenedByApp;
}
return null;
}
async function getBestUnbootedSimulatorAsync({
osType
}) {
const defaultUdid = _getDefaultSimulatorDeviceUDID();
if (defaultUdid && !osType) {
return defaultUdid;
}
const simulators = await getSelectableSimulatorsAsync({
osType
});
if (!simulators.length) {
// TODO: Prompt to install the simulators
throw new Error(`No ${osType || 'iOS'} devices available in Simulator.app`);
}
// If the default udid is defined, then check to ensure its osType matches the required os.
if (defaultUdid) {
const defaultSimulator = simulators.find(device => device.udid === defaultUdid);
if ((defaultSimulator === null || defaultSimulator === void 0 ? void 0 : defaultSimulator.osType) === osType) {
return defaultUdid;
}
}
// Return first selectable device.
return simulators[0].udid;
}
async function getBestSimulatorAsync({
osType
}) {
const simulatorOpenedByApp = await getBestBootedSimulatorAsync({
osType
});
if (simulatorOpenedByApp) {
return simulatorOpenedByApp.udid;
}
return await getBestUnbootedSimulatorAsync({
osType
});
}
/**
* Get all simulators supported by Expo (iOS only).
*/
async function getSelectableSimulatorsAsync({
osType = 'iOS'
} = {}) {
const simulators = await _internal().SimControl.listSimulatorDevicesAsync();
return simulators.filter(device => device.isAvailable && device.osType === osType);
}
// TODO: Delete
async function getBootedSimulatorsAsync() {
const simulators = await _internal().SimControl.listSimulatorDevicesAsync();
return simulators.filter(device => device.state === 'Booted');
}
// TODO: Delete
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;
}
}
function _getDefaultSimulatorDeviceUDID() {
try {
const defaultDeviceUDID = (0, _child_process().execSync)(`defaults read com.apple.iphonesimulator CurrentDeviceUDID`, {
stdio: 'pipe'
}).toString();
return defaultDeviceUDID.trim();
} catch {
return null;
}
}
async function activateSimulatorWindowAsync() {
// TODO: Focus the individual window
return await osascript().execAsync(`tell application "Simulator" to activate`);
}
async function closeSimulatorAppAsync() {
return await osascript().execAsync('tell application "Simulator" to quit');
}
async function isExpoClientInstalledOnSimulatorAsync({
udid
}) {
return !!(await _internal().SimControl.getContainerPathAsync({
udid,
bundleIdentifier: EXPO_GO_BUNDLE_IDENTIFIER
}));
}
async function waitForExpoClientInstalledOnSimulatorAsync({
udid
}) {
if (await isExpoClientInstalledOnSimulatorAsync({
udid
})) {
return true;
} else {
await (0, _internal().delayAsync)(100);
return await waitForExpoClientInstalledOnSimulatorAsync({
udid
});
}
}
async function waitForExpoClientUninstalledOnSimulatorAsync({
udid
}) {
if (!(await isExpoClientInstalledOnSimulatorAsync({
udid
}))) {
return true;
} else {
await (0, _internal().delayAsync)(100);
return await waitForExpoClientInstalledOnSimulatorAsync({
udid
});
}
}
async function expoVersionOnSimulatorAsync({
udid
}) {
const localPath = await _internal().SimControl.getContainerPathAsync({
udid,
bundleIdentifier: EXPO_GO_BUNDLE_IDENTIFIER
});
if (!localPath) {
return null;
}
const regex = /Exponent-([0-9.]+).*\.app$/;
const regexMatch = regex.exec(localPath);
if (!regexMatch) {
return null;
}
let matched = regexMatch[1];
// If the value is matched like 1.0.0. then remove the trailing dot.
if (matched.endsWith('.')) {
matched = matched.substr(0, matched.length - 1);
}
return matched;
}
async function doesExpoClientNeedUpdatedAsync(simulator, sdkVersion) {
var _clientForSdk$version;
// Test that upgrading works by returning true
// return true;
const versions = await (0, _profileMethod().profileMethod)(_internal().Versions.versionsAsync)();
const clientForSdk = await (0, _profileMethod().profileMethod)(getClientForSDK)(sdkVersion);
const latestVersionForSdk = (_clientForSdk$version = clientForSdk === null || clientForSdk === void 0 ? void 0 : clientForSdk.version) !== null && _clientForSdk$version !== void 0 ? _clientForSdk$version : versions.iosVersion;
const installedVersion = await expoVersionOnSimulatorAsync(simulator);
if (installedVersion && _semver().default.lt(installedVersion, latestVersionForSdk)) {
return true;
}
return false;
}
// If specific URL given just always download it and don't use cache
async function _downloadSimulatorAppAsync(url, downloadProgressCallback) {
if (!url) {
const versions = await _internal().Versions.versionsAsync();
url = versions.iosUrl;
}
const filename = _path().default.parse(url).name;
const dir = _path().default.join(simulatorCacheDirectory(), `${filename}.app`);
if (await _fsExtra().default.pathExists(dir)) {
const filesInDir = await _fsExtra().default.readdir(dir);
if (filesInDir.length > 0) {
return dir;
} else {
_fsExtra().default.removeSync(dir);
}
}
_fsExtra().default.mkdirpSync(dir);
try {
await (0, _internal().downloadAppAsync)(url, dir, {
extract: true
}, downloadProgressCallback);
} catch (e) {
_fsExtra().default.removeSync(dir);
throw e;
}
return dir;
}
// url: Optional URL of Exponent.app tarball to download
async function installExpoOnSimulatorAsync({
url,
simulator,
version
}) {
let warningTimer;
const setWarningTimer = () => {
if (warningTimer) {
clearTimeout(warningTimer);
}
return setTimeout(() => {
_internal().Logger.global.info('');
_internal().Logger.global.info('This download is taking longer than expected. You can also try downloading the clients from the website at https://expo.dev/tools');
}, INSTALL_WARNING_TIMEOUT);
};
_internal().Logger.notifications.info({
code: _internal().LoadingEvent.START_PROGRESS_BAR
}, 'Downloading the Expo Go app [:bar] :percent :etas');
warningTimer = setWarningTimer();
const dir = await _downloadSimulatorAppAsync(url, progress => {
_internal().Logger.notifications.info({
code: _internal().LoadingEvent.TICK_PROGRESS_BAR
}, progress);
});
_internal().Logger.notifications.info({
code: _internal().LoadingEvent.STOP_PROGRESS_BAR
});
const message = version ? `Installing Expo Go ${version} on ${simulator.name}` : `Installing Expo Go on ${simulator.name}`;
_internal().Logger.notifications.info({
code: _internal().LoadingEvent.START_LOADING
}, message);
warningTimer = setWarningTimer();
const result = await _internal().SimControl.installAsync({
udid: simulator.udid,
dir
});
_internal().Logger.notifications.info({
code: _internal().LoadingEvent.STOP_LOADING
});
clearTimeout(warningTimer);
return result;
}
async function uninstallExpoAppFromSimulatorAsync({
udid
} = {}) {
try {
_internal().Logger.global.info('Uninstalling Expo Go from iOS simulator.');
await _internal().SimControl.uninstallAsync({
udid,
bundleIdentifier: EXPO_GO_BUNDLE_IDENTIFIER
});
} catch (e) {
var _e$message;
if (!((_e$message = e.message) !== null && _e$message !== void 0 && _e$message.includes('No devices are booted.'))) {
_internal().Logger.global.error(e);
throw e;
}
}
}
function simulatorCacheDirectory() {
const dotExpoHomeDirectory = _internal().UserSettings.dotExpoHomeDirectory();
const dir = _path().default.join(dotExpoHomeDirectory, 'ios-simulator-app-cache');
_fsExtra().default.mkdirpSync(dir);
return dir;
}
async function upgradeExpoAsync(options = {}) {
if (!(await isSimulatorInstalledAsync())) {
return false;
}
const simulator = await ensureSimulatorOpenAsync(options);
await uninstallExpoAppFromSimulatorAsync(simulator);
const installResult = await installExpoOnSimulatorAsync({
url: options.url,
version: options.version,
simulator
});
if (installResult.status !== 0) {
return false;
}
if (_lastUrl) {
_internal().Logger.global.info(`\u203A Opening ${_chalk().default.underline(_lastUrl)} in Expo Go`);
await Promise.all([
// Open the Simulator.app app
(0, _ensureSimulatorAppRunningAsync().ensureSimulatorAppRunningAsync)(simulator),
// Launch the project in the simulator, this can be parallelized for some reason.
_internal().SimControl.openURLAsync({
udid: simulator.udid,
url: _lastUrl
})]);
_lastUrl = null;
}
return true;
}
async function openUrlInSimulatorSafeAsync({
url,
udid,
isDetached = false,
sdkVersion,
devClient = false,
projectRoot,
exp,
skipNativeLogs = false
}) {
if (!exp) {
exp = (0, _profileMethod().profileMethod)(_config().getConfig)(projectRoot, {
skipSDKVersionRequirement: true
}).exp;
}
let simulator = null;
try {
simulator = await (0, _profileMethod().profileMethod)(ensureSimulatorOpenAsync)({
udid
});
} catch (error) {
return {
success: false,
msg: error.message
};
}
_internal().Logger.global.info(`\u203A Opening ${_chalk().default.underline(url)} on ${_chalk().default.bold(simulator.name)}`);
let bundleIdentifier = EXPO_GO_BUNDLE_IDENTIFIER;
try {
if (devClient) {
bundleIdentifier = await (0, _profileMethod().profileMethod)(_internal().BundleIdentifier.configureBundleIdentifierAsync)(projectRoot, exp);
await (0, _profileMethod().profileMethod)(assertDevClientInstalledAsync)(simulator, bundleIdentifier);
if (!skipNativeLogs) {
// stream logs before opening the client.
await streamLogsAsync({
udid: simulator.udid,
bundleIdentifier
});
}
} else if (_internal().Env.isInterstitiaLPageEnabled() && !devClient && (0, _internal().isDevClientPackageInstalled)(projectRoot)) {
await (0, _profileMethod().profileMethod)(ensureExpoClientInstalledAsync)(simulator, sdkVersion);
const devClientBundlerIdentifier = await (0, _profileMethod().profileMethod)(_internal().BundleIdentifier.configureBundleIdentifierAsync)(projectRoot, exp);
const isDevClientInstalled = await isDevClientInstalledAsync(simulator, devClientBundlerIdentifier);
if (isDevClientInstalled) {
// Everything is installed, we can present the interstitial page.
bundleIdentifier = ''; // it will open browser.
} else {
// The development build isn't available. So let's fall back to Expo Go.
_internal().Logger.global.warn(`\u203A The 'expo-dev-client' package is installed, but a development build isn't available.\nYour app will open in Expo Go instead. If you want to use the development build, please install it on the simulator first.\n${(0, _internal().learnMore)('https://docs.expo.dev/development/build/')}`);
// Generate a new deep link into Expo Go.
const newProjectUrl = await constructDeepLinkAsync(projectRoot, undefined, false, false);
if (!newProjectUrl) {
// This shouldn't happen.
throw Error('Could not generate a deep link for your project.');
}
url = newProjectUrl;
_internal().Logger.global.debug(`iOS project url: ${url}`);
_lastUrl = url;
}
} else if (!isDetached) {
await (0, _profileMethod().profileMethod)(ensureExpoClientInstalledAsync)(simulator, sdkVersion);
_lastUrl = url;
}
await Promise.all([
// Open the Simulator.app app, and bring it to the front
(0, _profileMethod().profileMethod)(async () => {
var _simulator;
await (0, _ensureSimulatorAppRunningAsync().ensureSimulatorAppRunningAsync)({
udid: (_simulator = simulator) === null || _simulator === void 0 ? void 0 : _simulator.udid
});
activateSimulatorWindowAsync();
}, 'parallel: ensureSimulatorAppRunningAsync')(),
// Launch the project in the simulator, this can be parallelized for some reason.
(0, _profileMethod().profileMethod)(_internal().SimControl.openURLAsync, 'parallel: openURLAsync')({
udid: simulator.udid,
url
})]);
} catch (e) {
if (e.status === 194) {
// An error was encountered processing the command (domain=NSOSStatusErrorDomain, code=-10814):
// The operation couldnt be completed. (OSStatus error -10814.)
//
// This can be thrown when no app conforms to the URI scheme that we attempted to open.
return {
success: false,
msg: `Device ${simulator.name} (${simulator.udid}) has no app to handle the URI: ${url}`
};
}
if (e.isXDLError) {
// Hit some internal error, don't try again.
// This includes Xcode license errors
// Logger.global.error(e.message);
return {
success: false,
msg: `${e.toString()}`
};
}
return {
success: false,
msg: `${e.toString()}`
};
}
_internal().Analytics.logEvent('Open Url on Device', {
platform: 'ios'
});
return {
success: true,
device: simulator,
bundleIdentifier
};
}
async function assertDevClientInstalledAsync(simulator, bundleIdentifier) {
if (!(await _internal().SimControl.getContainerPathAsync({
udid: simulator.udid,
bundleIdentifier
}))) {
throw new Error(`The development client (${bundleIdentifier}) for this project is not installed. ` + `Please build and install the client on the simulator first.\n${(0, _internal().learnMore)('https://docs.expo.dev/clients/distribution-for-ios/#building-for-ios')}`);
}
}
async function isDevClientInstalledAsync(simulator, bundleIdentifier) {
try {
await assertDevClientInstalledAsync(simulator, bundleIdentifier);
} catch {
return false;
}
return true;
}
// Keep a list of simulator UDIDs so we can prevent asking multiple times if a user wants to upgrade.
// This can prevent annoying interactions when they don't want to upgrade for whatever reason.
const hasPromptedToUpgrade = {};
async function ensureExpoClientInstalledAsync(simulator, sdkVersion) {
let isInstalled = await isExpoClientInstalledOnSimulatorAsync(simulator);
if (isInstalled && !hasPromptedToUpgrade[simulator.udid]) {
// Only prompt/check for updates once per simulator in a single run.
hasPromptedToUpgrade[simulator.udid] = true;
if (await (0, _profileMethod().profileMethod)(doesExpoClientNeedUpdatedAsync)(simulator, sdkVersion)) {
const confirm = await _internal().Prompts.confirmAsync({
initial: true,
message: `Expo Go on ${simulator.name} is outdated, would you like to upgrade?`
});
if (confirm) {
// TODO: Is there any downside to skipping the uninstall step?
// await uninstallExpoAppFromSimulatorAsync(simulator);
// await waitForExpoClientUninstalledOnSimulatorAsync(simulator);
isInstalled = false;
}
}
}
// If it's still "not installed" then install it (again).
if (!isInstalled) {
const iosClient = await getClientForSDK(sdkVersion);
await installExpoOnSimulatorAsync({
simulator,
...iosClient
});
await waitForExpoClientInstalledOnSimulatorAsync(simulator);
}
}
async function getClientForSDK(sdkVersionString) {
if (!sdkVersionString) {
return null;
}
const sdkVersion = (await _internal().Versions.sdkVersionsAsync())[sdkVersionString];
if (!sdkVersion) {
return null;
}
return {
url: sdkVersion.iosClientUrl,
version: sdkVersion.iosClientVersion
};
}
async function resolveApplicationIdAsync(projectRoot) {
var _exp$ios;
// Check xcode project
try {
const bundleId = await _configPlugins().IOSConfig.BundleIdentifier.getBundleIdentifierFromPbxproj(projectRoot);
if (bundleId) {
return bundleId;
}
} catch {}
// Check Info.plist
try {
const infoPlistPath = _configPlugins().IOSConfig.Paths.getInfoPlistPath(projectRoot);
const data = await _plist().default.parse(_fsExtra().default.readFileSync(infoPlistPath, 'utf8'));
if (data.CFBundleIdentifier && !data.CFBundleIdentifier.startsWith('$(')) {
return data.CFBundleIdentifier;
}
} catch {}
// Check Expo config
const {
exp
} = (0, _config().getConfig)(projectRoot, {
skipSDKVersionRequirement: true
});
return (_exp$ios = exp.ios) === null || _exp$ios === void 0 ? void 0 : _exp$ios.bundleIdentifier;
}
async function constructDeepLinkAsync(projectRoot, scheme, devClient, shouldGenerateInterstitialPage = true) {
if (_internal().Env.isInterstitiaLPageEnabled() && !devClient && (0, _internal().isDevClientPackageInstalled)(projectRoot) && shouldGenerateInterstitialPage) {
return _internal().UrlUtils.constructLoadingUrlAsync(projectRoot, 'ios', 'localhost');
} else {
try {
return await _internal().UrlUtils.constructDeepLinkAsync(projectRoot, {
// Don't pass a `hostType` or ngrok will break.
scheme
});
} catch (e) {
if (devClient) {
return null;
}
throw e;
}
}
}
async function openProjectAsync({
projectRoot,
shouldPrompt,
devClient,
udid,
scheme,
skipNativeLogs,
applicationId
}) {
var _device2;
if (!(await (0, _profileMethod().profileMethod)(isSimulatorInstalledAsync)())) {
return {
success: false,
error: 'Unable to verify Xcode and Simulator installation.'
};
}
const projectUrl = await constructDeepLinkAsync(projectRoot, scheme, devClient);
_internal().Logger.global.debug(`iOS project url: ${projectUrl}`);
const {
exp
} = (0, _config().getConfig)(projectRoot, {
skipSDKVersionRequirement: true
});
let device = null;
if (!udid && shouldPrompt) {
const devices = await getSelectableSimulatorsAsync();
device = await promptForSimulatorAsync(devices);
if (!device) {
return {
success: false,
error: 'escaped'
};
}
} else {
device = await ensureSimulatorOpenAsync({
udid
});
}
// No URL, and is devClient
if (!projectUrl) {
var _applicationId;
applicationId = (_applicationId = applicationId) !== null && _applicationId !== void 0 ? _applicationId : await resolveApplicationIdAsync(projectRoot);
_internal().Logger.global.debug(`Open iOS project from app id: ${applicationId}`);
if (!applicationId) {
return {
success: false,
error: 'Cannot resolve bundle identifier or URI scheme to open the native iOS app.\nBuild the native app with `expo run:ios` or `eas build -p ios`'
};
}
_internal().Logger.global.info(`\u203A Opening ${_chalk().default.underline(applicationId)} on ${_chalk().default.bold(device.name)}`);
const result = await _internal().SimControl.openBundleIdAsync({
udid: device.udid,
bundleIdentifier: applicationId
}).catch(error => {
if ('status' in error) {
return error;
}
throw error;
});
if (result.status === 0) {
var _device;
await (0, _ensureSimulatorAppRunningAsync().ensureSimulatorAppRunningAsync)({
udid: (_device = device) === null || _device === void 0 ? void 0 : _device.udid
});
activateSimulatorWindowAsync();
} else {
let errorMessage = `Couldn't open iOS app with ID "${applicationId}" on device "${device.name}".`;
if (result.status === 4) {
errorMessage += `\nThe app might not be installed, try installing it with: ${_chalk().default.bold(`expo run:ios -d ${device.udid}`)}`;
}
errorMessage += _chalk().default.gray(`\n${result.stderr}`);
return {
success: false,
error: errorMessage
};
}
return {
success: true,
udid: device.udid,
bundleIdentifier: applicationId,
// TODO: Remove this hack
url: ''
};
}
const result = await (0, _profileMethod().profileMethod)(openUrlInSimulatorSafeAsync)({
udid: (_device2 = device) === null || _device2 === void 0 ? void 0 : _device2.udid,
url: projectUrl,
sdkVersion: exp.sdkVersion,
isDetached: !!exp.isDetached,
devClient,
exp,
projectRoot,
skipNativeLogs
});
if (result.success) {
return {
success: true,
url: projectUrl,
udid: result.device.udid,
bundleIdentifier: result.bundleIdentifier
};
}
return {
success: result.success,
error: result.msg
};
}
async function streamLogsAsync({
bundleIdentifier,
udid
}) {
if (_internal().SimControlLogs.isStreamingLogs(udid)) {
return;
}
const imageName = await _internal().SimControlLogs.getImageNameFromBundleIdentifierAsync(udid, bundleIdentifier);
if (imageName) {
// Attach simulator log observer
_internal().SimControlLogs.streamLogs({
pid: imageName,
udid
});
}
}
async function openWebProjectAsync({
projectRoot,
shouldPrompt
}) {
var _device3;
if (!(await isSimulatorInstalledAsync())) {
return {
success: false,
error: 'Unable to verify Xcode and Simulator installation.'
};
}
const projectUrl = await _internal().Webpack.getUrlAsync(projectRoot);
if (projectUrl === null) {
return {
success: false,
error: `The web project has not been started yet`
};
}
let device = null;
if (shouldPrompt) {
const devices = await getSelectableSimulatorsAsync();
device = await promptForSimulatorAsync(devices);
if (!device) {
return {
success: false,
error: 'escaped'
};
}
}
const result = await openUrlInSimulatorSafeAsync({
url: projectUrl,
udid: (_device3 = device) === null || _device3 === void 0 ? void 0 : _device3.udid,
isDetached: true,
projectRoot
});
if (result.success) {
// run out of sync
activateSimulatorWindowAsync();
return {
success: true,
url: projectUrl
};
}
return {
success: result.success,
error: result.msg
};
}
/**
* Sort the devices so the last simulator that was opened (user's default) is the first suggested.
*
* @param devices
*/
async function sortDefaultDeviceToBeginningAsync(devices, osType) {
const defaultUdid = await getBestSimulatorAsync({
osType
});
if (defaultUdid) {
let iterations = 0;
while (devices[0].udid !== defaultUdid && iterations < devices.length) {
devices.push(devices.shift());
iterations++;
}
}
return devices;
}
async function promptForSimulatorAsync(devices, osType) {
devices = await sortDefaultDeviceToBeginningAsync(devices, osType);
// TODO: Bail on non-interactive
const results = await promptForDeviceAsync(devices);
return results ? devices.find(({
udid
}) => results === udid) : null;
}
async function promptForDeviceAsync(devices) {
// TODO: provide an option to add or download more simulators
// TODO: Add support for physical devices too.
// Pause interactions on the TerminalUI
_internal().Prompts.pauseInteractions();
const {
value
} = await (0, _prompts().default)({
type: 'autocomplete',
name: 'value',
limit: 11,
message: 'Select a simulator',
choices: devices.map(item => {
const isActive = item.state === 'Booted';
const format = isActive ? _chalk().default.bold : text => text;
return {
title: `${format(item.name)} ${_chalk().default.dim(`(${item.osVersion})`)}`,
value: item.udid
};
}),
suggest: (input, choices) => {
const regex = new RegExp(input, 'i');
return choices.filter(choice => regex.test(choice.title));
}
});
// Resume interactions on the TerminalUI
_internal().Prompts.resumeInteractions();
return value;
}
//# sourceMappingURL=Simulator.js.map