Decouple iOS devices from Store / core

Summary: Decouple iOS device detection from Redux

Reviewed By: timur-valiev

Differential Revision: D30309258

fbshipit-source-id: 74b4e3dd2e6b83fcefc75909794c39bfc8c987cf
This commit is contained in:
Michel Weststrate
2021-08-17 07:50:43 -07:00
committed by Facebook GitHub Bot
parent 3736cbc480
commit ea58f2b050
9 changed files with 291 additions and 333 deletions

View File

@@ -357,10 +357,11 @@ class SettingsSheet extends Component<Props, State> {
}
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
({settingsState, launcherSettingsState, application}) => ({
({settingsState, launcherSettingsState, connections}) => ({
settings: settingsState,
launcherSettings: launcherSettingsState,
isXcodeDetected: application.xcodeCommandLineToolsDetected,
isXcodeDetected:
connections.flipperServer?.ios.xcodeCommandLineToolsDetected ?? false,
}),
{updateSettings, updateLauncherSettings},
)(withTrackingScope(SettingsSheet));

View File

@@ -16,11 +16,15 @@ import Client from '../Client';
import {notification} from 'antd';
export default async (store: Store, logger: Logger) => {
const {enableAndroid, androidHome} = store.getState().settingsState;
const {enableAndroid, androidHome, idbPath, enableIOS, enablePhysicalIOS} =
store.getState().settingsState;
const server = new FlipperServer(
{
enableAndroid,
androidHome,
idbPath,
enableIOS,
enablePhysicalIOS,
serverPorts: store.getState().application.serverPorts,
},
store,

View File

@@ -86,7 +86,6 @@ export type State = {
serverPorts: ServerPorts;
launcherMsg: LauncherMsg;
statusMessages: Array<string>;
xcodeCommandLineToolsDetected: boolean;
pastedToken?: string;
};
@@ -149,12 +148,6 @@ export type Action =
type: 'REMOVE_STATUS_MSG';
payload: {msg: string; sender: string};
}
| {
type: 'SET_XCODE_DETECTED';
payload: {
isDetected: boolean;
};
}
| {
type: 'SET_PASTED_TOKEN';
payload?: string;
@@ -177,7 +170,6 @@ export const initialState: () => State = () => ({
message: '',
},
statusMessages: [],
xcodeCommandLineToolsDetected: false,
trackingTimeline: [],
});
@@ -288,8 +280,6 @@ export default function reducer(
return {...state, statusMessages};
}
return state;
} else if (action.type === 'SET_XCODE_DETECTED') {
return {...state, xcodeCommandLineToolsDetected: action.payload.isDetected};
} else if (action.type === 'SET_PASTED_TOKEN') {
return produce(state, (draft) => {
draft.pastedToken = action.payload;
@@ -368,11 +358,6 @@ export const removeStatusMessage = (payload: StatusMessageType): Action => ({
payload,
});
export const setXcodeDetected = (isDetected: boolean): Action => ({
type: 'SET_XCODE_DETECTED',
payload: {isDetected},
});
export const setPastedToken = (pastedToken?: string): Action => ({
type: 'SET_PASTED_TOKEN',
payload: pastedToken,

View File

@@ -21,8 +21,7 @@ import {launchEmulator} from '../../server/devices/android/AndroidDevice';
import {Layout, renderReactRoot, withTrackingScope} from 'flipper-plugin';
import {Provider} from 'react-redux';
import {
launchSimulator,
getSimulators,
launchSimulator, // TODO: move to iOSDeviceManager
IOSDeviceParams,
} from '../../server/devices/ios/iOSDeviceManager';
import GK from '../../fb-stubs/GK';
@@ -39,19 +38,16 @@ export function showEmulatorLauncher(store: Store) {
}
function LaunchEmulatorContainer({onClose}: {onClose: () => void}) {
const store = useStore();
const flipperServer = useStore((state) => state.connections.flipperServer);
return (
<LaunchEmulatorDialog
onClose={onClose}
getSimulators={getSimulators.bind(store)}
getSimulators={() => flipperServer!.ios.getSimulators(false)}
getEmulators={() => flipperServer!.android.getAndroidEmulators()}
/>
);
}
type GetSimulators = typeof getSimulators;
export const LaunchEmulatorDialog = withTrackingScope(
function LaunchEmulatorDialog({
onClose,
@@ -59,7 +55,7 @@ export const LaunchEmulatorDialog = withTrackingScope(
getEmulators,
}: {
onClose: () => void;
getSimulators: GetSimulators;
getSimulators: () => Promise<IOSDeviceParams[]>;
getEmulators: () => Promise<string[]>;
}) {
const iosEnabled = useStore((state) => state.settingsState.enableIOS);
@@ -74,7 +70,8 @@ export const LaunchEmulatorDialog = withTrackingScope(
if (!iosEnabled) {
return;
}
getSimulators(store, false).then((emulators) => {
getSimulators()
.then((emulators) => {
setIosEmulators(
emulators.filter(
(device) =>
@@ -82,6 +79,9 @@ export const LaunchEmulatorDialog = withTrackingScope(
device.deviceTypeIdentifier?.match(/iPhone|iPad/i),
),
);
})
.catch((e) => {
console.warn('Failed to find simulators', e);
});
}, [iosEnabled, getSimulators, store]);
@@ -94,7 +94,7 @@ export const LaunchEmulatorDialog = withTrackingScope(
setAndroidEmulators(emulators);
})
.catch((e) => {
console.warn('Failed to find emulatiors', e);
console.warn('Failed to find emulators', e);
});
}, [androidEnabled, getEmulators]);

View File

@@ -24,7 +24,7 @@ import {
setActiveSheet,
} from '../reducers/application';
import {AndroidDeviceManager} from './devices/android/androidDeviceManager';
import iOSDevice from './devices/ios/iOSDeviceManager';
import {IOSDeviceManager} from './devices/ios/iOSDeviceManager';
import metroDevice from './devices/metro/metroDeviceManager';
import desktopDevice from './devices/desktop/desktopDeviceManager';
import BaseDevice from './devices/BaseDevice';
@@ -45,6 +45,9 @@ type FlipperServerEvents = {
export interface FlipperServerConfig {
enableAndroid: boolean;
androidHome: string;
enableIOS: boolean;
idbPath: string;
enablePhysicalIOS: boolean;
serverPorts: ServerPorts;
}
@@ -54,6 +57,9 @@ type ServerState = 'pending' | 'starting' | 'started' | 'error' | 'closed';
const defaultConfig: FlipperServerConfig = {
androidHome: '',
enableAndroid: false,
enableIOS: false,
enablePhysicalIOS: false,
idbPath: '',
serverPorts: {
insecure: -1,
secure: -1,
@@ -78,6 +84,7 @@ export class FlipperServer {
private readonly devices = new Map<string, BaseDevice>();
state: ServerState = 'pending';
android: AndroidDeviceManager;
ios: IOSDeviceManager;
// TODO: remove store argument
constructor(
@@ -89,6 +96,7 @@ export class FlipperServer {
this.config = {...defaultConfig, ...config};
const server = (this.server = new ServerController(this));
this.android = new AndroidDeviceManager(this);
this.ios = new IOSDeviceManager(this);
server.addListener('new-client', (client: Client) => {
this.emit('client-connected', client);
@@ -200,7 +208,7 @@ export class FlipperServer {
async startDeviceListeners() {
this.disposers.push(
await this.android.watchAndroidDevices(),
iOSDevice(this.store, this.logger),
await this.ios.watchIOSDevices(),
metroDevice(this),
desktopDevice(this),
);
@@ -271,6 +279,10 @@ export class FlipperServer {
return Array.from(this.devices.keys());
}
getDevices(): BaseDevice[] {
return Array.from(this.devices.values());
}
public async close() {
this.server.close();
for (const device of this.devices.values()) {

View File

@@ -10,7 +10,6 @@
import AndroidDevice from './AndroidDevice';
import KaiOSDevice from './KaiOSDevice';
import child_process from 'child_process';
import BaseDevice from '../BaseDevice';
import {getAdbClient} from './adbClient';
import which from 'which';
import {promisify} from 'util';
@@ -22,11 +21,10 @@ import {notNull} from '../../utils/typeUtils';
export class AndroidDeviceManager {
// cache emulator path
private emulatorPath: string | undefined;
private devices: Map<string, AndroidDevice> = new Map();
constructor(public flipperServer: FlipperServer) {}
createDevice(
private createDevice(
adbClient: ADBClient,
device: any,
): Promise<AndroidDevice | undefined> {
@@ -106,15 +104,6 @@ export class AndroidDeviceManager {
});
}
async getActiveAndroidDevices(): Promise<Array<BaseDevice>> {
const client = await getAdbClient(this.flipperServer.config);
const androidDevices = await client.listDevices();
const devices = await Promise.all(
androidDevices.map((device) => this.createDevice(client, device)),
);
return devices.filter(Boolean) as any;
}
async getEmulatorPath(): Promise<string> {
if (this.emulatorPath) {
return this.emulatorPath;
@@ -154,7 +143,9 @@ export class AndroidDeviceManager {
});
}
async getRunningEmulatorName(id: string): Promise<string | null | undefined> {
private async getRunningEmulatorName(
id: string,
): Promise<string | null | undefined> {
return new Promise((resolve, reject) => {
const port = id.replace('emulator-', '');
// The GNU version of netcat doesn't terminate after 1s when
@@ -184,8 +175,13 @@ export class AndroidDeviceManager {
.then((tracker) => {
tracker.on('error', (err) => {
if (err.message === 'Connection closed') {
this.unregisterDevices(Array.from(this.devices.keys()));
console.warn('adb server was shutdown');
this.flipperServer
.getDevices()
.filter((d) => d instanceof AndroidDevice)
.forEach((d) => {
this.flipperServer.unregisterDevice(d.serial);
});
setTimeout(() => {
this.watchAndroidDevices();
}, 500);
@@ -202,14 +198,14 @@ export class AndroidDeviceManager {
tracker.on('change', async (device) => {
if (device.type === 'offline') {
this.unregisterDevices([device.id]);
this.flipperServer.unregisterDevice(device.id);
} else {
this.registerDevice(client, device);
}
});
tracker.on('remove', (device) => {
this.unregisterDevices([device.id]);
this.flipperServer.unregisterDevice(device.id);
});
})
.catch((err: {code: string}) => {
@@ -224,7 +220,7 @@ export class AndroidDeviceManager {
}
}
async registerDevice(adbClient: ADBClient, deviceData: any) {
private async registerDevice(adbClient: ADBClient, deviceData: any) {
const androidDevice = await this.createDevice(adbClient, deviceData);
if (!androidDevice) {
return;
@@ -232,10 +228,4 @@ export class AndroidDeviceManager {
this.flipperServer.registerDevice(androidDevice);
}
unregisterDevices(serials: Array<string>) {
serials.forEach((serial) => {
this.flipperServer.unregisterDevice(serial);
});
}
}

View File

@@ -7,14 +7,12 @@
* @format
*/
import {
parseXcodeFromCoreSimPath,
getAllPromisesForQueryingDevices,
} from '../iOSDeviceManager';
import {parseXcodeFromCoreSimPath} from '../iOSDeviceManager';
import configureStore from 'redux-mock-store';
import {State, createRootReducer} from '../../../../reducers/index';
import {getInstance} from '../../../../fb-stubs/Logger';
import {IOSBridge} from '../IOSBridge';
import {FlipperServer} from '../../../FlipperServer';
const mockStore = configureStore<State, {}>([])(
createRootReducer()(undefined, {type: 'INIT'}),
@@ -62,21 +60,15 @@ test('test parseXcodeFromCoreSimPath from standard locations', () => {
});
test('test getAllPromisesForQueryingDevices when xcode detected', () => {
const promises = getAllPromisesForQueryingDevices(
mockStore,
logger,
{} as IOSBridge,
true,
);
const flipperServer = new FlipperServer({}, mockStore, getInstance());
flipperServer.ios.iosBridge = {} as IOSBridge;
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(true);
expect(promises.length).toEqual(3);
});
test('test getAllPromisesForQueryingDevices when xcode is not detected', () => {
const promises = getAllPromisesForQueryingDevices(
mockStore,
logger,
{} as IOSBridge,
false,
);
const flipperServer = new FlipperServer({}, mockStore, getInstance());
flipperServer.ios.iosBridge = {} as IOSBridge;
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(false);
expect(promises.length).toEqual(1);
});

View File

@@ -8,9 +8,6 @@
*/
import {ChildProcess} from 'child_process';
import {Store} from '../../../reducers/index';
import {setXcodeDetected} from '../../../reducers/application';
import {Logger} from '../../../fb-interfaces/Logger';
import type {DeviceType} from 'flipper-plugin';
import {promisify} from 'util';
import path from 'path';
@@ -18,14 +15,13 @@ import child_process from 'child_process';
const execFile = child_process.execFile;
import iosUtil from './iOSContainerUtility';
import IOSDevice from './IOSDevice';
import {addErrorNotification} from '../../../reducers/notifications';
import {getStaticPath} from '../../../utils/pathUtils';
import {destroyDevice} from '../../../reducers/connections';
import {
ERR_NO_IDB_OR_XCODE_AVAILABLE,
IOSBridge,
makeIOSBridge,
} from './IOSBridge';
import {FlipperServer} from '../../FlipperServer';
type iOSSimulatorDevice = {
state: 'Booted' | 'Shutdown' | 'Shutting Down';
@@ -45,8 +41,6 @@ export type IOSDeviceParams = {
const exec = promisify(child_process.exec);
let portForwarders: Array<ChildProcess> = [];
function isAvailable(simulator: iOSSimulatorDevice): boolean {
// For some users "availability" is set, for others it's "isAvailable"
// It's not clear which key is set, so we are checking both.
@@ -58,7 +52,10 @@ function isAvailable(simulator: iOSSimulatorDevice): boolean {
);
}
const portforwardingClient = getStaticPath(
export class IOSDeviceManager {
private portForwarders: Array<ChildProcess> = [];
private portforwardingClient = getStaticPath(
path.join(
'PortForwardingMacApp.app',
'Contents',
@@ -66,14 +63,30 @@ const portforwardingClient = getStaticPath(
'PortForwardingMacApp',
),
);
iosBridge: IOSBridge | undefined;
private xcodeVersionMismatchFound = false;
public xcodeCommandLineToolsDetected = false;
function forwardPort(port: number, multiplexChannelPort: number) {
constructor(private flipperServer: FlipperServer) {
if (typeof window !== 'undefined') {
window.addEventListener('beforeunload', () => {
this.portForwarders.forEach((process) => process.kill());
});
}
}
private forwardPort(port: number, multiplexChannelPort: number) {
const childProcess = execFile(
portforwardingClient,
this.portforwardingClient,
[`-portForward=${port}`, `-multiplexChannelPort=${multiplexChannelPort}`],
(err, stdout, stderr) => {
// This happens on app reloads and doesn't need to be treated as an error.
console.warn('Port forwarding app failed to start', err, stdout, stderr);
console.warn(
'Port forwarding app failed to start',
err,
stdout,
stderr,
);
},
);
console.log('Port forwarding app started', childProcess);
@@ -86,42 +99,36 @@ function forwardPort(port: number, multiplexChannelPort: number) {
return childProcess;
}
function startDevicePortForwarders(): void {
if (portForwarders.length > 0) {
private startDevicePortForwarders(): void {
if (this.portForwarders.length > 0) {
// Only ever start them once.
return;
}
// start port forwarding server for real device connections
portForwarders = [forwardPort(8089, 8079), forwardPort(8088, 8078)];
// TODO: ports should be picked up from flipperServer.config?
this.portForwarders = [
this.forwardPort(8089, 8079),
this.forwardPort(8088, 8078),
];
}
if (typeof window !== 'undefined') {
window.addEventListener('beforeunload', () => {
portForwarders.forEach((process) => process.kill());
});
}
export function getAllPromisesForQueryingDevices(
store: Store,
logger: Logger,
iosBridge: IOSBridge,
getAllPromisesForQueryingDevices(
isXcodeDetected: boolean,
): Array<Promise<any>> {
const {config} = this.flipperServer;
const promArray = [
getActiveDevices(
store.getState().settingsState.idbPath,
store.getState().settingsState.enablePhysicalIOS,
).then((devices: IOSDeviceParams[]) => {
console.log('Active iOS devices:', devices);
processDevices(store, logger, iosBridge, devices, 'physical');
}),
getActiveDevices(config.idbPath, config.enablePhysicalIOS).then(
(devices: IOSDeviceParams[]) => {
this.processDevices(devices, 'physical');
},
),
];
if (isXcodeDetected) {
promArray.push(
...[
checkXcodeVersionMismatch(store),
getSimulators(store, true).then((devices) => {
processDevices(store, logger, iosBridge, devices, 'emulator');
this.checkXcodeVersionMismatch(),
this.getSimulators(true).then((devices) => {
this.processDevices(devices, 'emulator');
}),
],
);
@@ -129,32 +136,21 @@ export function getAllPromisesForQueryingDevices(
return promArray;
}
async function queryDevices(
store: Store,
logger: Logger,
iosBridge: IOSBridge,
): Promise<any> {
private async queryDevices(): Promise<any> {
const isXcodeInstalled = await iosUtil.isXcodeDetected();
return Promise.all(
getAllPromisesForQueryingDevices(
store,
logger,
iosBridge,
isXcodeInstalled,
),
);
return Promise.all(this.getAllPromisesForQueryingDevices(isXcodeInstalled));
}
function processDevices(
store: Store,
logger: Logger,
iosBridge: IOSBridge,
private processDevices(
activeDevices: IOSDeviceParams[],
type: 'physical' | 'emulator',
) {
const {connections} = store.getState();
const currentDeviceIDs: Set<string> = new Set(
connections.devices
if (!this.iosBridge) {
throw new Error('iOS bridge not yet initialized');
}
const currentDeviceIDs = new Set(
this.flipperServer
.getDevices()
.filter(
(device) =>
device instanceof IOSDevice &&
@@ -168,44 +164,51 @@ function processDevices(
if (currentDeviceIDs.has(udid)) {
currentDeviceIDs.delete(udid);
} else {
// clean up offline device
destroyDevice(store, logger, udid);
logger.track('usage', 'register-device', {
os: 'iOS',
type: type,
name: name,
serial: udid,
});
const iOSDevice = new IOSDevice(iosBridge, udid, type, name);
iOSDevice.loadDevicePlugins(
store.getState().plugins.devicePlugins,
store.getState().connections.enabledDevicePlugins,
);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: iOSDevice,
});
const iOSDevice = new IOSDevice(this.iosBridge, udid, type, name);
this.flipperServer.registerDevice(iOSDevice);
}
}
currentDeviceIDs.forEach((id) => {
const device = store
.getState()
.connections.devices.find((device) => device.serial === id);
device?.disconnect();
this.flipperServer.unregisterDevice(id);
});
}
function getDeviceSetPath() {
return process.env.DEVICE_SET_PATH
? ['--set', process.env.DEVICE_SET_PATH]
: [];
public async watchIOSDevices() {
// TODO: pull this condition up
if (!this.flipperServer.config.enableIOS) {
return;
}
try {
const isDetected = await iosUtil.isXcodeDetected();
this.xcodeCommandLineToolsDetected = isDetected;
if (this.flipperServer.config.enablePhysicalIOS) {
this.startDevicePortForwarders();
}
try {
// Awaiting the promise here to trigger immediate error handling.
this.iosBridge = await makeIOSBridge(
this.flipperServer.config.idbPath,
isDetected,
);
this.queryDevicesForever();
} catch (err) {
// This case is expected if both Xcode and idb are missing.
if (err.message === ERR_NO_IDB_OR_XCODE_AVAILABLE) {
console.warn(
'Failed to init iOS device. You may want to disable iOS support in the settings.',
err,
);
} else {
console.error('Failed to initialize iOS dispatcher:', err);
}
}
} catch (err) {
console.error('Error while querying iOS devices:', err);
}
}
export function getSimulators(
store: Store,
bootedOnly: boolean,
): Promise<Array<IOSDeviceParams>> {
getSimulators(bootedOnly: boolean): Promise<Array<IOSDeviceParams>> {
return promisify(execFile)(
'xcrun',
['simctl', ...getDeviceSetPath(), 'list', 'devices', '--json'],
@@ -232,17 +235,64 @@ export function getSimulators(
.catch((e: Error) => {
console.warn('Failed to query simulators:', e);
if (e.message.includes('Xcode license agreements')) {
store.dispatch(
addErrorNotification(
'Xcode license requires approval',
this.flipperServer.emit('notification', {
type: 'error',
title: 'Xcode license requires approval',
description:
'The Xcode license agreement has changed. You need to either open Xcode and agree to the terms or run `sudo xcodebuild -license` in a Terminal to allow simulators to work with Flipper.',
),
);
});
}
return Promise.resolve([]);
});
}
private queryDevicesForever() {
return this.queryDevices()
.then(() => {
// It's important to schedule the next check AFTER the current one has completed
// to avoid simultaneous queries which can cause multiple user input prompts.
setTimeout(() => this.queryDevicesForever(), 3000);
})
.catch((err) => {
console.warn('Failed to continuously query devices:', err);
});
}
async checkXcodeVersionMismatch() {
if (this.xcodeVersionMismatchFound) {
return;
}
try {
let {stdout: xcodeCLIVersion} = await exec('xcode-select -p');
xcodeCLIVersion = xcodeCLIVersion.trim();
const {stdout} = await exec('ps aux | grep CoreSimulator');
for (const line of stdout.split('\n')) {
const match = parseXcodeFromCoreSimPath(line);
const runningVersion =
match && match.length > 0 ? match[0].trim() : null;
if (runningVersion && runningVersion !== xcodeCLIVersion) {
const errorMessage = `Xcode version mismatch: Simulator is running from "${runningVersion}" while Xcode CLI is "${xcodeCLIVersion}". Running "xcode-select --switch ${runningVersion}" can fix this. For example: "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer"`;
this.flipperServer.emit('notification', {
type: 'error',
title: 'Xcode version mismatch',
description: '' + errorMessage,
});
this.xcodeVersionMismatchFound = true;
break;
}
}
} catch (e) {
console.error('Failed to determine Xcode version:', e);
}
}
}
function getDeviceSetPath() {
return process.env.DEVICE_SET_PATH
? ['--set', process.env.DEVICE_SET_PATH]
: [];
}
export async function launchSimulator(udid: string): Promise<any> {
await promisify(execFile)(
'xcrun',
@@ -262,88 +312,8 @@ function getActiveDevices(
});
}
function queryDevicesForever(
store: Store,
logger: Logger,
iosBridge: IOSBridge,
) {
return queryDevices(store, logger, iosBridge)
.then(() => {
// It's important to schedule the next check AFTER the current one has completed
// to avoid simultaneous queries which can cause multiple user input prompts.
setTimeout(() => queryDevicesForever(store, logger, iosBridge), 3000);
})
.catch((err) => {
console.warn('Failed to continuously query devices:', err);
});
}
export function parseXcodeFromCoreSimPath(
line: string,
): RegExpMatchArray | null {
return line.match(/\/[\/\w@)(\-\+]*\/Xcode[^/]*\.app\/Contents\/Developer/);
}
let xcodeVersionMismatchFound = false;
async function checkXcodeVersionMismatch(store: Store) {
if (xcodeVersionMismatchFound) {
return;
}
try {
let {stdout: xcodeCLIVersion} = await exec('xcode-select -p');
xcodeCLIVersion = xcodeCLIVersion.trim();
const {stdout} = await exec('ps aux | grep CoreSimulator');
for (const line of stdout.split('\n')) {
const match = parseXcodeFromCoreSimPath(line);
const runningVersion = match && match.length > 0 ? match[0].trim() : null;
if (runningVersion && runningVersion !== xcodeCLIVersion) {
const errorMessage = `Xcode version mismatch: Simulator is running from "${runningVersion}" while Xcode CLI is "${xcodeCLIVersion}". Running "xcode-select --switch ${runningVersion}" can fix this. For example: "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer"`;
store.dispatch(
addErrorNotification('Xcode version mismatch', errorMessage),
);
xcodeVersionMismatchFound = true;
break;
}
}
} catch (e) {
console.error('Failed to determine Xcode version:', e);
}
}
export default (store: Store, logger: Logger) => {
if (!store.getState().settingsState.enableIOS) {
return;
}
iosUtil
.isXcodeDetected()
.then(async (isDetected) => {
store.dispatch(setXcodeDetected(isDetected));
if (store.getState().settingsState.enablePhysicalIOS) {
startDevicePortForwarders();
}
try {
// Awaiting the promise here to trigger immediate error handling.
return await makeIOSBridge(
store.getState().settingsState.idbPath,
isDetected,
);
} catch (err) {
// This case is expected if both Xcode and idb are missing.
if (err.message === ERR_NO_IDB_OR_XCODE_AVAILABLE) {
console.warn(
'Failed to init iOS device. You may want to disable iOS support in the settings.',
err,
);
} else {
console.error('Failed to initialize iOS dispatcher:', err);
}
}
})
.then(
(iosBridge) => iosBridge && queryDevicesForever(store, logger, iosBridge),
)
.catch((err) => {
console.error('Error while querying iOS devices:', err);
});
};

View File

@@ -29,6 +29,7 @@ import {Client as ADBClient} from 'adbkit';
import archiver from 'archiver';
import {timeout} from 'flipper-plugin';
import {v4 as uuid} from 'uuid';
import {isTest} from '../../utils/isProduction';
export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW';
@@ -581,7 +582,10 @@ export default class CertificateProvider {
});
}
ensureCertificateAuthorityExists(): Promise<void> {
async ensureCertificateAuthorityExists(): Promise<void> {
if (isTest()) {
return;
}
if (!fs.existsSync(caKey)) {
return this.generateCertificateAuthority();
}