Fix daemon device discovery crash when Xcode isn't installed (#60546) (#60717)

Co-authored-by: Jenn Magder <magder@google.com>
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 2d052cd..bdece05 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -68,6 +68,9 @@
         'Control of iOS devices or simulators only supported on macOS.'
       );
     }
+    if (!_xcdevice.isInstalled) {
+      return;
+    }
 
     deviceNotifier ??= ItemListNotifier<Device>();
 
@@ -76,7 +79,7 @@
 
     // cancel any outstanding subscriptions.
     await _observedDeviceEventsSubscription?.cancel();
-    _observedDeviceEventsSubscription = _xcdevice.observedDeviceEvents().listen(
+    _observedDeviceEventsSubscription = _xcdevice.observedDeviceEvents()?.listen(
       _onDeviceEvent,
       onError: (dynamic error, StackTrace stack) {
         _logger.printTrace('Process exception running xcdevice observe:\n$error\n$stack');
diff --git a/packages/flutter_tools/test/general.shard/ios/devices_test.dart b/packages/flutter_tools/test/general.shard/ios/devices_test.dart
index 4d39209..a5d9d2e 100644
--- a/packages/flutter_tools/test/general.shard/ios/devices_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/devices_test.dart
@@ -324,6 +324,19 @@
       );
     });
 
+    testWithoutContext('start polling without Xcode', () async {
+      final IOSDevices iosDevices = IOSDevices(
+        platform: macPlatform,
+        xcdevice: mockXcdevice,
+        iosWorkflow: mockIosWorkflow,
+        logger: logger,
+      );
+      when(mockXcdevice.isInstalled).thenReturn(false);
+
+      await iosDevices.startPolling();
+      verifyNever(mockXcdevice.getAvailableIOSDevices());
+    });
+
     testWithoutContext('start polling', () async {
       final IOSDevices iosDevices = IOSDevices(
         platform: macPlatform,