From 8be0848aae953a8d4965052b596fd07268df30c4 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 8 May 2026 09:25:49 -0400 Subject: [PATCH 1/2] [video_player] Add missing swift_version to podspec Adds a `swift_version` to the podspec of `video_player_avfoundation`, to fix a build error when building with CocoaPods in a project that doesn't itself have a Swift version set. Updates the repo tooling `podspec-check` command to check for this, so that we don't forget it in future migrations. Also fixes some missing ignores in `podspec-check` that caused false positives when running the check locally in a tree that has built plugins. Fixes https://github.com/flutter/flutter/issues/186232 --- .../video_player_avfoundation/CHANGELOG.md | 4 + .../darwin/video_player_avfoundation.podspec | 1 + .../video_player_avfoundation/pubspec.yaml | 2 +- .../tool/lib/src/podspec_check_command.dart | 29 +++++- .../tool/test/podspec_check_command_test.dart | 93 ++++++++++++++++++- 5 files changed, 123 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 42a8ea8b0a26..af4f99d56320 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.9.6 + +* Adds a Swift version to fix a potential build issue when using CocoaPods. + ## 2.9.5 * Converts portions of the native code to Swift for improved maintainability. diff --git a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec index 416a4461570c..3c2ddd29b2cf 100644 --- a/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec +++ b/packages/video_player/video_player_avfoundation/darwin/video_player_avfoundation.podspec @@ -23,6 +23,7 @@ Downloaded by pub (not CocoaPods). s.ios.deployment_target = '13.0' s.osx.deployment_target = '10.15' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' s.xcconfig = { 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 762d8d08eadd..9b462f0376fe 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS and macOS implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.9.5 +version: 2.9.6 environment: sdk: ^3.10.0 diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index 24f0d80f3461..fe36e107e646 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -75,7 +75,9 @@ class PodspecCheckCommand extends PackageLoopingCommand { } if (await _hasIOSSwiftCode(package)) { - print('iOS Swift code found, checking for search paths settings...'); + print( + 'iOS Swift code found, checking for search paths settings and Swift version...', + ); for (final podspec in podspecs) { if (_isPodspecMissingSearchPaths(podspec)) { const workaroundBlock = r''' @@ -97,6 +99,20 @@ class PodspecCheckCommand extends PackageLoopingCommand { ); errors.add(podspec.basename); } + + if (_isPodspecMissingSwiftVersion(podspec)) { + final String path = getRelativePosixPath( + podspec, + from: package.directory, + ); + printError( + '$path is missing Swift version configuration. Any iOS ' + 'plugin implementation that contains Swift implementation code ' + 'needs to contain a Swift version. For example:\n\n' + "s.swift_version = '5.0'", + ); + errors.add(podspec.basename); + } } } @@ -124,6 +140,8 @@ class PodspecCheckCommand extends PackageLoopingCommand { return path.extension(filename) == '.podspec' && filename != 'Flutter.podspec' && filename != 'FlutterMacOS.podspec' && + // Ignore build intermediates, such as transitive pod dependencies. + !entity.absolute.path.contains('/build/') && !entity.path.contains('packages/pigeon/platform_tests/'); }).toList(); @@ -177,6 +195,11 @@ class PodspecCheckCommand extends PackageLoopingCommand { if (relativePath.startsWith('example/')) { return false; } + // Ignore build intermediates. + if (relativePath.contains('/build/') || + relativePath.startsWith('build/')) { + return false; + } // Ignore test code. if (relativePath.contains('/Tests/') || relativePath.contains('/RunnerTests/') || @@ -226,4 +249,8 @@ class PodspecCheckCommand extends PackageLoopingCommand { ); return manifestBundling.hasMatch(podspec.readAsStringSync()); } + + bool _isPodspecMissingSwiftVersion(File podspec) { + return !RegExp(r'swift_version\s*=').hasMatch(podspec.readAsStringSync()); + } } diff --git a/script/tool/test/podspec_check_command_test.dart b/script/tool/test/podspec_check_command_test.dart index dbd7fe9b276f..f0ee53853880 100644 --- a/script/tool/test/podspec_check_command_test.dart +++ b/script/tool/test/podspec_check_command_test.dart @@ -14,21 +14,22 @@ import 'package:test/test.dart'; import 'mocks.dart'; import 'util.dart'; -/// Adds a fake podspec to [plugin]'s [platform] directory. +/// Adds a fake podspec to [plugin]'s [subdirectory] directory. /// /// If [includeSwiftWorkaround] is set, the xcconfig additions to make Swift /// libraries work in apps that have no Swift will be included. If /// [scopeSwiftWorkaround] is set, it will be specific to the iOS configuration. void _writeFakePodspec( RepositoryPackage plugin, - String platform, { + String subdirectory, { bool includeSwiftWorkaround = false, bool scopeSwiftWorkaround = false, bool includePrivacyManifest = false, + bool includeSwiftVersion = true, }) { final String pluginName = plugin.directory.basename; final File file = plugin.directory - .childDirectory(platform) + .childDirectory(subdirectory) .childFile('$pluginName.podspec'); final swiftWorkaround = includeSwiftWorkaround ? ''' @@ -43,6 +44,7 @@ void _writeFakePodspec( s.resource_bundles = {'$pluginName' => ['Resources/PrivacyInfo.xcprivacy']} ''' : ''; + final swiftVersion = includeSwiftVersion ? "s.swift_version = '5.0'" : ''; file.createSync(recursive: true); file.writeAsStringSync(''' # @@ -66,7 +68,7 @@ Wraps NSUserDefaults, providing a persistent store for simple key-value pairs. s.osx.deployment_target = '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } $swiftWorkaround - s.swift_version = '5.0' + $swiftVersion $privacyManifest end @@ -452,6 +454,54 @@ void main() { }, ); + test( + 'ignores Swift files in the build directory when determining whether the plugin uses Swift', + () async { + final RepositoryPackage plugin = createFakePlugin( + 'plugin1', + packagesDir, + extraFiles: [ + 'ios/Classes/SomeObjC.h', + 'ios/Classes/SomeObjC.m', + 'build/SomeSwiftFile.swift', + 'ios/build/AnotherSwiftFile.swift', + ], + ); + _writeFakePodspec(plugin, 'ios'); + + final List output = await runCapturingPrint(runner, [ + 'podspec-check', + ]); + + expect( + output, + containsAllInOrder([contains('Ran for 1 package(s)')]), + ); + }, + ); + + test('does not check for search paths in build output', () async { + final RepositoryPackage plugin = createFakePlugin( + 'plugin1', + packagesDir, + extraFiles: [ + 'ios/Classes/SomeSwift.swift', + 'ios/plugin1/Package.swift', + ], + ); + _writeFakePodspec(plugin, 'ios', includeSwiftWorkaround: true); + _writeFakePodspec(plugin, 'ios/build/some_pod'); + + final List output = await runCapturingPrint(runner, [ + 'podspec-check', + ]); + + expect( + output, + containsAllInOrder([contains('Ran for 1 package(s)')]), + ); + }); + test('passes if the search paths workaround is present', () async { final RepositoryPackage plugin = createFakePlugin( 'plugin1', @@ -629,5 +679,40 @@ void main() { containsAllInOrder([contains('Ran for 1 package(s)')]), ); }); + + test('fails when a Swift plugin is missing a Swift version', () async { + final RepositoryPackage plugin = createFakePlugin( + 'plugin1', + packagesDir, + platformSupport: { + Platform.iOS: const PlatformDetails(PlatformSupport.inline), + }, + extraFiles: ['ios/Classes/SomeSwift.swift'], + ); + _writeFakePodspec( + plugin, + 'ios', + includeSwiftVersion: false, + includeSwiftWorkaround: true, + includePrivacyManifest: true, + ); + + Error? commandError; + final List output = await runCapturingPrint( + runner, + ['podspec-check'], + errorHandler: (Error e) { + commandError = e; + }, + ); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('missing Swift version configuration'), + ]), + ); + }); }); } From 85372be6d9f42a11e729e84a4c3f511d881e6fc1 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Fri, 8 May 2026 10:02:50 -0400 Subject: [PATCH 2/2] Gemini feedback --- script/tool/lib/src/podspec_check_command.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index fe36e107e646..735afd2a6580 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -137,11 +137,15 @@ class PodspecCheckCommand extends PackageLoopingCommand { File entity, ) { final String filename = entity.basename; + final String relativePath = getRelativePosixPath( + entity, + from: package.directory, + ); return path.extension(filename) == '.podspec' && filename != 'Flutter.podspec' && filename != 'FlutterMacOS.podspec' && // Ignore build intermediates, such as transitive pod dependencies. - !entity.absolute.path.contains('/build/') && + !relativePath.split('/').contains('build') && !entity.path.contains('packages/pigeon/platform_tests/'); }).toList(); @@ -196,8 +200,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { return false; } // Ignore build intermediates. - if (relativePath.contains('/build/') || - relativePath.startsWith('build/')) { + if (relativePath.split('/').contains('build')) { return false; } // Ignore test code. @@ -251,6 +254,6 @@ class PodspecCheckCommand extends PackageLoopingCommand { } bool _isPodspecMissingSwiftVersion(File podspec) { - return !RegExp(r'swift_version\s*=').hasMatch(podspec.readAsStringSync()); + return !RegExp(r'\bswift_version\s*=').hasMatch(podspec.readAsStringSync()); } }