diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index 12e3843de..55d7f821a 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -176,6 +176,7 @@ export type IOSDeviceParams = { udid: string; type: DeviceType; name: string; + osVersion?: string; deviceTypeIdentifier?: string; state?: string; }; diff --git a/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx b/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx index 5487b727b..566e9b4aa 100644 --- a/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx +++ b/desktop/flipper-server-core/src/devices/ios/IOSBridge.tsx @@ -42,6 +42,16 @@ interface IOSInstalledAppDescriptor { debuggableStatus: boolean; } +function getOSVersionFromXCRunOutput(s: string): string | undefined { + // E.g. 'com.apple.CoreSimulator.SimRuntime.iOS-16-1' + const match = s.match( + /com\.apple\.CoreSimulator\.SimRuntime\.iOS-(\d+)-(\d+)/, + ); + if (match) { + return `${match[1]}.${match[2]}`; + } +} + export interface IOSBridge { startLogListener: ( udid: string, @@ -284,21 +294,23 @@ export class SimctlBridge implements IOSBridge { '--json', ]) .then(({stdout}) => JSON.parse(stdout!.toString()).devices) - .then((simulatorDevices: Array) => { - const simulators = Object.values(simulatorDevices).flat(); - return simulators - .filter( - (simulator) => - (!bootedOnly || simulator.state === 'Booted') && - isSimulatorAvailable(simulator), - ) - .map((simulator) => { - return { - ...simulator, - type: 'emulator', - } as IOSDeviceParams; - }); - }); + .then((simulatorDevices: {[key: string]: Array}) => + Object.keys(simulatorDevices).flatMap((key: string) => + simulatorDevices[key] + .filter( + (simulator: iOSSimulatorDevice) => + (!bootedOnly || simulator.state === 'Booted') && + isSimulatorAvailable(simulator), + ) + .map((simulator: iOSSimulatorDevice) => { + return { + ...simulator, + type: 'emulator', + osVersion: getOSVersionFromXCRunOutput(key), + } as IOSDeviceParams; + }), + ), + ); } async launchSimulator(udid: string): Promise { diff --git a/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx b/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx index 307053f47..9311657af 100644 --- a/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx +++ b/desktop/flipper-server-core/src/devices/ios/__tests__/iOSDevice.node.tsx @@ -24,11 +24,13 @@ const fakeDevices: IOSDeviceParams[] = [ udid: 'luke', type: 'emulator', name: 'Luke', + osVersion: '16.4', }, { udid: 'yoda', type: 'emulator', name: 'Yoda', + osVersion: '16.4', }, ]; const fakeExistingDevices = [ diff --git a/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx b/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx index 935f36d60..56a8f559c 100644 --- a/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx +++ b/desktop/flipper-server-core/src/devices/ios/iOSContainerUtility.tsx @@ -48,6 +48,7 @@ export type DeviceTarget = { udid: string; type: DeviceType; name: string; + osVersion?: string; }; let idbDeviceListing = 0; @@ -253,6 +254,7 @@ function parseIdbTarget(line: string): DeviceTarget | undefined { ? 'emulator' : ('physical' as DeviceType), name: parsed.name, + osVersion: parsed.os_version, }; } diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx index 19b4eaf3c..bf52d69ee 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx @@ -229,6 +229,7 @@ export const LaunchEmulatorDialog = withTrackingScope( .then(onClose) }> {device.name} + {device.osVersion ? ` (${device.osVersion})` : ''} ))