@@ -66,7 +66,9 @@ class IOSSimulatorUtils {
6666 return < IOSSimulator > [];
6767 }
6868
69- final List <SimDevice > connected = await _simControl.getConnectedDevices ();
69+ final List <SimDevice > connected = (await _simControl.getAvailableDevices ())
70+ .where ((SimDevice device) => device.isBooted)
71+ .toList ();
7072 return connected.map <IOSSimulator >((SimDevice device) {
7173 return IOSSimulator (
7274 device.udid,
@@ -77,6 +79,26 @@ class IOSSimulatorUtils {
7779 );
7880 }).toList ();
7981 }
82+
83+ Future <List <IOSSimulator >> getAvailableDevices () async {
84+ if (! _xcode.isInstalledAndMeetsVersionCheck) {
85+ return < IOSSimulator > [];
86+ }
87+
88+ final List <SimDevice > available = await _simControl.getAvailableDevices ();
89+ return available
90+ .map <IOSSimulator >((SimDevice device) {
91+ return IOSSimulator (
92+ device.udid,
93+ name: device.name,
94+ simControl: _simControl,
95+ simulatorCategory: device.category,
96+ xcode: _xcode,
97+ );
98+ })
99+ .where ((IOSSimulator simulator) => simulator.isSupported ())
100+ .toList ();
101+ }
80102}
81103
82104/// A wrapper around the `simctl` command line tool.
@@ -89,6 +111,23 @@ class SimControl {
89111 _xcode = xcode,
90112 _processUtils = ProcessUtils (processManager: processManager, logger: logger);
91113
114+ /// Create a [SimControl] for testing.
115+ ///
116+ /// Defaults to a buffer logger.
117+ @visibleForTesting
118+ factory SimControl .test ({
119+ @required ProcessManager processManager,
120+ Logger logger,
121+ Xcode xcode,
122+ }) {
123+ logger ?? = BufferLogger .test ();
124+ return SimControl (
125+ logger: logger,
126+ xcode: xcode,
127+ processManager: processManager,
128+ );
129+ }
130+
92131 final Logger _logger;
93132 final ProcessUtils _processUtils;
94133 final Xcode _xcode;
@@ -160,10 +199,10 @@ class SimControl {
160199 return devices;
161200 }
162201
163- /// Returns all the connected simulator devices.
164- Future <List <SimDevice >> getConnectedDevices () async {
202+ /// Returns all the available simulator devices.
203+ Future <List <SimDevice >> getAvailableDevices () async {
165204 final List <SimDevice > simDevices = await getDevices ();
166- return simDevices.where ((SimDevice device) => device.isBooted ).toList ();
205+ return simDevices.where ((SimDevice device) => device.isAvailable ).toList ();
167206 }
168207
169208 Future <bool > isInstalled (String deviceId, String appId) {
@@ -234,6 +273,17 @@ class SimControl {
234273 return result;
235274 }
236275
276+ Future <RunResult > boot (String deviceId) {
277+ return _processUtils.run (
278+ < String > [
279+ ..._xcode.xcrunCommand (),
280+ 'simctl' ,
281+ 'boot' ,
282+ deviceId,
283+ ],
284+ );
285+ }
286+
237287 Future <void > takeScreenshot (String deviceId, String outputPath) async {
238288 try {
239289 await _processUtils.run (
@@ -296,7 +346,11 @@ class SimDevice {
296346 final Map <String , dynamic > data;
297347
298348 String get state => data['state' ]? .toString ();
299- String get availability => data['availability' ]? .toString ();
349+
350+ bool get isAvailable =>
351+ data['isAvailable' ] == true ||
352+ data['availability' ]? .toString () == '(available)' ;
353+
300354 String get name => data['name' ]? .toString ();
301355 String get udid => data['udid' ]? .toString ();
302356
@@ -394,29 +448,32 @@ class IOSSimulator extends Device {
394448 @override
395449 bool isSupported () {
396450 if (! globals.platform.isMacOS) {
397- _supportMessage = 'iOS devices require a Mac host machine.' ;
398451 return false ;
399452 }
400453
401454 // Check if the device is part of a blocked category.
402455 // We do not yet support WatchOS or tvOS devices.
403456 final RegExp blocklist = RegExp (r'Apple (TV|Watch)' , caseSensitive: false );
404457 if (blocklist.hasMatch (name)) {
405- _supportMessage = 'Flutter does not support Apple TV or Apple Watch.' ;
406458 return false ;
407459 }
408460 return true ;
409461 }
410462
411- String _supportMessage;
463+ Future <bool > boot () async {
464+ final RunResult result = await _simControl.boot (id);
412465
413- @override
414- String supportMessage () {
415- if (isSupported ()) {
416- return 'Supported' ;
466+ if (result.exitCode == 0 ) {
467+ return true ;
468+ }
469+ // 149 exit code means the device is already booted. Ignore this error.
470+ if (result.exitCode == 149 ) {
471+ globals.printTrace ('Simulator "$id " already booted.' );
472+ return true ;
417473 }
418474
419- return _supportMessage ?? 'Unknown' ;
475+ globals.logger.printError ('$result ' );
476+ return false ;
420477 }
421478
422479 @override
0 commit comments