// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import '../base/user_messages.dart'; import '../base/version.dart'; import '../build_info.dart'; import '../doctor_validator.dart'; import '../ios/simulators.dart'; import 'xcode.dart'; String _iOSSimulatorMissing(String version) => ''' iOS $version Simulator not installed; this may be necessary for iOS and macOS development. To download and install the platform, open Xcode, select Xcode > Settings > Platforms, and click the GET button for the required platform. For more information, please visit: https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes'''; class XcodeValidator extends DoctorValidator { XcodeValidator({ required Xcode xcode, required IOSSimulatorUtils iosSimulatorUtils, required UserMessages userMessages, }) : _xcode = xcode, _iosSimulatorUtils = iosSimulatorUtils, _userMessages = userMessages, super('Xcode - develop for iOS and macOS'); final Xcode _xcode; final IOSSimulatorUtils _iosSimulatorUtils; final UserMessages _userMessages; @override Future<ValidationResult> validate() async { final List<ValidationMessage> messages = <ValidationMessage>[]; ValidationType xcodeStatus = ValidationType.missing; String? xcodeVersionInfo; final String? xcodeSelectPath = _xcode.xcodeSelectPath; if (_xcode.isInstalled) { xcodeStatus = ValidationType.success; if (xcodeSelectPath != null) { messages.add(ValidationMessage(_userMessages.xcodeLocation(xcodeSelectPath))); } final String? versionText = _xcode.versionText; if (versionText != null) { xcodeVersionInfo = versionText; if (xcodeVersionInfo.contains(',')) { xcodeVersionInfo = xcodeVersionInfo.substring(0, xcodeVersionInfo.indexOf(',')); } } if (_xcode.buildVersion != null) { messages.add(ValidationMessage('Build ${_xcode.buildVersion}')); } if (!_xcode.isInstalledAndMeetsVersionCheck) { xcodeStatus = ValidationType.partial; messages.add(ValidationMessage.error(_userMessages.xcodeOutdated(xcodeRequiredVersion.toString()))); } else if (!_xcode.isRecommendedVersionSatisfactory) { xcodeStatus = ValidationType.partial; messages.add(ValidationMessage.hint(_userMessages.xcodeRecommended(xcodeRecommendedVersion.toString()))); } if (!_xcode.eulaSigned) { xcodeStatus = ValidationType.partial; messages.add(ValidationMessage.error(_userMessages.xcodeEula)); } if (!_xcode.isSimctlInstalled) { xcodeStatus = ValidationType.partial; messages.add(ValidationMessage.error(_userMessages.xcodeMissingSimct)); } final ValidationMessage? missingSimulatorMessage = await _validateSimulatorRuntimeInstalled(); if (missingSimulatorMessage != null) { xcodeStatus = ValidationType.partial; messages.add(missingSimulatorMessage); } } else { xcodeStatus = ValidationType.missing; if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) { messages.add(ValidationMessage.error(_userMessages.xcodeMissing)); } else { messages.add(ValidationMessage.error(_userMessages.xcodeIncomplete)); } } return ValidationResult(xcodeStatus, messages, statusInfo: xcodeVersionInfo); } /// Validate the Xcode-installed iOS simulator SDK has a corresponding iOS /// simulator runtime installed. /// /// Starting with Xcode 15, the iOS simulator runtime is no longer downloaded /// with Xcode and must be downloaded and installed separately. /// iOS applications cannot be run without it. Future<ValidationMessage?> _validateSimulatorRuntimeInstalled() async { // Skip this validation if Xcode is not installed, Xcode is a version less // than 15, simctl is not installed, or if the EULA is not signed. if (!_xcode.isInstalled || _xcode.currentVersion == null || _xcode.currentVersion!.major < 15 || !_xcode.isSimctlInstalled || !_xcode.eulaSigned) { return null; } final Version? platformSDKVersion = await _xcode.sdkPlatformVersion(EnvironmentType.simulator); if (platformSDKVersion == null) { return const ValidationMessage.error('Unable to find the iPhone Simulator SDK.'); } final List<IOSSimulatorRuntime> runtimes = await _iosSimulatorUtils.getAvailableIOSRuntimes(); if (runtimes.isEmpty) { return const ValidationMessage.error('Unable to get list of installed Simulator runtimes.'); } // Verify there is a simulator runtime installed matching the // iphonesimulator SDK major version. try { runtimes.firstWhere( (IOSSimulatorRuntime runtime) => runtime.version?.major == platformSDKVersion.major, ); } on StateError { return ValidationMessage.hint(_iOSSimulatorMissing(platformSDKVersion.toString())); } return null; } }