| // 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; |
| } |
| } |