iOS: In case Xcode is installed but the version is too old. Advise the user to update.
diff --git a/packages/flutter_tools/lib/src/ios/device_ios.dart b/packages/flutter_tools/lib/src/ios/device_ios.dart index 83d4695..0225701 100644 --- a/packages/flutter_tools/lib/src/ios/device_ios.dart +++ b/packages/flutter_tools/lib/src/ios/device_ios.dart
@@ -248,7 +248,7 @@ IOSSimulator(String id, { this.name }) : super(id); static List<IOSSimulator> getAttachedDevices() { - if (!xcode.isInstalled) + if (!xcode.isInstalledAndMeetsVersionCheck) return <IOSSimulator>[]; return SimControl.getConnectedDevices().map((SimDevice device) {
diff --git a/packages/flutter_tools/lib/src/ios/ios_workflow.dart b/packages/flutter_tools/lib/src/ios/ios_workflow.dart index 5cc4f70..ebbfb54 100644 --- a/packages/flutter_tools/lib/src/ios/ios_workflow.dart +++ b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
@@ -7,6 +7,7 @@ import '../base/process.dart'; import '../doctor.dart'; import '../globals.dart'; +import 'mac.dart'; class IOSWorkflow extends Workflow { IOSWorkflow() : super('iOS'); @@ -14,11 +15,11 @@ bool get appliesToHostPlatform => Platform.isMacOS; // We need xcode (+simctl) to list simulator devices, and idevice_id to list real devices. - bool get canListDevices => xcode.isInstalled; + bool get canListDevices => xcode.isInstalledAndMeetsVersionCheck; // We need xcode to launch simulator devices, and ideviceinstaller and ios-deploy // for real devices. - bool get canLaunchDevices => xcode.isInstalled; + bool get canLaunchDevices => xcode.isInstalledAndMeetsVersionCheck; ValidationResult validate() { Validator iosValidator = new Validator( @@ -27,7 +28,23 @@ ); Function _xcodeExists = () { - return xcode.isInstalled ? ValidationType.installed : ValidationType.missing; + if (xcode.isInstalledAndMeetsVersionCheck) { + return ValidationType.installed; + } + + if (xcode.isInstalled) { + return ValidationType.partial; + } + + return ValidationType.missing; + }; + + Function _xcodeVersionSatisfacotry = () { + if (xcode.isInstalledAndMeetsVersionCheck) { + return ValidationType.installed; + } + + return ValidationType.missing; }; Function _brewExists = () { @@ -44,11 +61,20 @@ return hasIdeviceId ? ValidationType.installed : ValidationType.missing; }; - iosValidator.addValidator(new Validator( + Validator xcodeValidator = new Validator( 'XCode', description: 'enable development for iOS devices', resolution: 'Download at https://developer.apple.com/xcode/download/', validatorFunction: _xcodeExists + ); + + iosValidator.addValidator(xcodeValidator); + + xcodeValidator.addValidator(new Validator( + 'XCode', + description: 'Xcode version is at least $kXcodeRequiredVersionMajor.$kXcodeRequiredVersionMinor', + resolution: 'Download the latest version or update via the Mac App Store', + validatorFunction: _xcodeVersionSatisfacotry )); Validator brewValidator = new Validator(
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index 47f5e79..d2aacc3 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -5,10 +5,49 @@ import '../base/context.dart'; import '../base/process.dart'; +const int kXcodeRequiredVersionMajor = 7; +const int kXcodeRequiredVersionMinor = 2; + class XCode { static void initGlobal() { context[XCode] = new XCode(); } - bool get isInstalled => exitsHappy(<String>['xcode-select', '--print-path']); + bool get isInstalledAndMeetsVersionCheck => isInstalled && xcodeVersionSatisfactory; + + bool _isInstalled; + bool get isInstalled { + if (_isInstalled != null) { + return _isInstalled; + } + + _isInstalled = exitsHappy(<String>['xcode-select', '--print-path']); + return _isInstalled; + } + + bool _xcodeVersionSatisfactory; + bool get xcodeVersionSatisfactory { + if (_xcodeVersionSatisfactory != null) { + return _xcodeVersionSatisfactory; + } + + try { + String output = runSync(<String>['xcodebuild', '-version']); + RegExp regex = new RegExp(r'Xcode ([0-9.]+)'); + + String version = regex.firstMatch(output).group(1); + List<String> components = version.split('.'); + + int major = int.parse(components[0]); + int minor = components.length == 1 ? 0 : int.parse(components[1]); + + _xcodeVersionSatisfactory = major >= kXcodeRequiredVersionMajor && minor >= kXcodeRequiredVersionMinor; + return _xcodeVersionSatisfactory; + } catch (error) { + _xcodeVersionSatisfactory = false; + return false; + } + + return false; + } }