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