1c4ef02aeSEvan Baconimport chalk from 'chalk'; 2c4ef02aeSEvan Baconimport { Ora } from 'ora'; 3c4ef02aeSEvan Baconimport os from 'os'; 4c4ef02aeSEvan Baconimport path from 'path'; 5c4ef02aeSEvan Bacon 6*8a424bebSJames Ideimport * as AppleDevice from './AppleDevice'; 7c4ef02aeSEvan Baconimport { ensureDirectory } from '../../../utils/dir'; 8c4ef02aeSEvan Baconimport { CommandError } from '../../../utils/errors'; 929128565SEvan Baconimport { isInteractive } from '../../../utils/interactive'; 10c4ef02aeSEvan Baconimport { ora } from '../../../utils/ora'; 11c4ef02aeSEvan Baconimport { confirmAsync } from '../../../utils/prompts'; 12c4ef02aeSEvan Bacon 13c4ef02aeSEvan Bacon/** Get the app_delta folder for faster subsequent rebuilds on devices. */ 14c4ef02aeSEvan Baconexport function getAppDeltaDirectory(bundleId: string): string { 15c4ef02aeSEvan Bacon // TODO: Maybe use .expo folder instead for debugging 16c4ef02aeSEvan Bacon // TODO: Reuse existing folder from xcode? 17c4ef02aeSEvan Bacon const deltaFolder = path.join(os.tmpdir(), 'ios', 'app-delta', bundleId); 18c4ef02aeSEvan Bacon ensureDirectory(deltaFolder); 19c4ef02aeSEvan Bacon return deltaFolder; 20c4ef02aeSEvan Bacon} 21c4ef02aeSEvan Bacon 22c4ef02aeSEvan Bacon/** 23c4ef02aeSEvan Bacon * Wraps the apple device method for installing and running an app, 24c4ef02aeSEvan Bacon * adds indicator and retry loop for when the device is locked. 25c4ef02aeSEvan Bacon */ 26c4ef02aeSEvan Baconexport async function installOnDeviceAsync(props: { 27c4ef02aeSEvan Bacon bundle: string; 28c4ef02aeSEvan Bacon bundleIdentifier: string; 29c4ef02aeSEvan Bacon appDeltaDirectory: string; 30c4ef02aeSEvan Bacon udid: string; 31c4ef02aeSEvan Bacon deviceName: string; 32c4ef02aeSEvan Bacon}): Promise<void> { 33c4ef02aeSEvan Bacon const { bundle, bundleIdentifier, appDeltaDirectory, udid, deviceName } = props; 34c4ef02aeSEvan Bacon let indicator: Ora | undefined; 35c4ef02aeSEvan Bacon 36c4ef02aeSEvan Bacon try { 37c4ef02aeSEvan Bacon // TODO: Connect for logs 38c4ef02aeSEvan Bacon await AppleDevice.runOnDevice({ 39c4ef02aeSEvan Bacon udid, 40c4ef02aeSEvan Bacon appPath: bundle, 41c4ef02aeSEvan Bacon bundleId: bundleIdentifier, 42c4ef02aeSEvan Bacon waitForApp: false, 43c4ef02aeSEvan Bacon deltaPath: appDeltaDirectory, 44c4ef02aeSEvan Bacon onProgress({ 45c4ef02aeSEvan Bacon status, 46c4ef02aeSEvan Bacon isComplete, 47c4ef02aeSEvan Bacon progress, 48c4ef02aeSEvan Bacon }: { 49c4ef02aeSEvan Bacon status: string; 50c4ef02aeSEvan Bacon isComplete: boolean; 51c4ef02aeSEvan Bacon progress: number; 52c4ef02aeSEvan Bacon }) { 53c4ef02aeSEvan Bacon if (!indicator) { 54c4ef02aeSEvan Bacon indicator = ora(status).start(); 55c4ef02aeSEvan Bacon } 56c4ef02aeSEvan Bacon indicator.text = `${chalk.bold(status)} ${progress}%`; 57c4ef02aeSEvan Bacon if (isComplete) { 58c4ef02aeSEvan Bacon indicator.succeed(); 59c4ef02aeSEvan Bacon } 60c4ef02aeSEvan Bacon }, 61c4ef02aeSEvan Bacon }); 62c4ef02aeSEvan Bacon } catch (error: any) { 63c4ef02aeSEvan Bacon if (indicator) { 64c4ef02aeSEvan Bacon indicator.fail(); 65c4ef02aeSEvan Bacon } 66c4ef02aeSEvan Bacon if (error.code === 'APPLE_DEVICE_LOCKED') { 67c4ef02aeSEvan Bacon // Get the app name from the binary path. 68c4ef02aeSEvan Bacon const appName = path.basename(bundle).split('.')[0] ?? 'app'; 69c4ef02aeSEvan Bacon if ( 7029128565SEvan Bacon isInteractive() && 71c4ef02aeSEvan Bacon (await confirmAsync({ 72c4ef02aeSEvan Bacon message: `Cannot launch ${appName} because the device is locked. Unlock ${deviceName} to continue...`, 73c4ef02aeSEvan Bacon initial: true, 74c4ef02aeSEvan Bacon })) 75c4ef02aeSEvan Bacon ) { 76c4ef02aeSEvan Bacon return installOnDeviceAsync(props); 77c4ef02aeSEvan Bacon } 78c4ef02aeSEvan Bacon throw new CommandError( 79c4ef02aeSEvan Bacon `Cannot launch ${appName} on ${deviceName} because the device is locked.` 80c4ef02aeSEvan Bacon ); 81c4ef02aeSEvan Bacon } 82c4ef02aeSEvan Bacon throw error; 83c4ef02aeSEvan Bacon } 84c4ef02aeSEvan Bacon} 85