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