Filter out SwiftPM schemes when fetching schemes#186006
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements a filtering mechanism for Xcode schemes to skip Swift Package Manager schemes, reducing the time spent probing for watchOS companion targets. It verifies the existence of an .xcscheme file in the host project's shared schemes directory. Tests were updated to reflect this requirement and a new test case was added. Feedback indicates that the current implementation may skip valid schemes in workspaces or those not marked as shared.
|
@lukemmtt Do you think this is something you have bandwidth for sometime this week or next? If not, I'll have my team take over so we can get this fixed ASAP. Let me know :) |
|
Thanks for the note! Yes, I'll take a stab at it today and let you know :) |
In iOS projects with a watchOS companion target and Swift Package Manager enabled, IosProject.containsWatchCompanion()'s fallback path iterates every entry in projectInfo.schemes and runs xcodebuild -showBuildSettings -destination generic/platform=watchOS for each one. With SwiftPM, that list contains a scheme per resolved package and plugin, none of which can be a watch companion. Each probe takes ~2-3 seconds, so on a real project this added ~160s (60 probes) before the actual build started. This change restricts the fallback to schemes that have an .xcscheme file under the host Xcode project's xcshareddata/xcschemes/ directory, so SwiftPM package schemes are skipped without an xcodebuild call. Fixes flutter#186004
Addresses review feedback: the previous filter only checked Runner.xcodeproj/xcshareddata/xcschemes, which would falsely skip projects whose watch scheme is shared at the workspace level (Runner.xcworkspace/xcshareddata/xcschemes) instead. The host-shared check now passes when an .xcscheme file exists in either the project's or the workspace's xcshareddata/xcschemes directory. SwiftPM package schemes still appear in neither, so they continue to be skipped. Adds a test that places the scheme only at the workspace level and asserts that containsWatchCompanion finds it.
Moves SwiftPM scheme filtering into XcodeProjectInterpreter.getInfo so callers receive an XcodeProjectInfo that excludes Flutter-generated Swift package schemes, direct plugin scheme names, plugin dash variants, and transitive Swift package schemes discovered under SourcePackages checkouts. This keeps watch companion detection from probing SwiftPM package schemes while avoiding a host .xcscheme file requirement for valid non-shared project schemes. Adds getInfo coverage for direct plugin schemes and transitive .swiftpm schemes, and restores watch companion tests to exercise generic non-default scheme probing.
28d6b49 to
f9975fc
Compare
|
Thanks again for the heads-up about #186378 landing; I’ve now rebased onto the latest The filtering logic has been moved earlier, into
I’ve also removed the earlier shared-scheme filter from Local testing against my TimeFinder app showed the scheme count reduced significantly as expected (67 → 6), and correctly detecting the watch companion (TimeFinderWatchApp) without probing unrelated plugin schemes (Patrol, Amplitude-Swift, etc). Happy to address any needed revisions as quickly as I can. Thanks for the attention to this Victoria! |
vashworth
left a comment
There was a problem hiding this comment.
This looks really great! Thanks for working on it! I think it just needs some doc comments and then should be good to go
…-swiftpm-scheme-scan
Compare schemes parsed from `xcodebuild -list` against ignored scheme candidates case-insensitively instead of generating sentence-cased plugin variants. The ignored set still enumerates known plugin spellings (`snake_case` and dashed), while casing is handled at lookup time. Add comments explaining why the watch companion fallback excludes generated Flutter SwiftPM package schemes, plugin scheme-name candidates, and schemes contributed by transitive SwiftPM checkouts. Addresses review feedback on flutter#186006.
|
Thanks for the thoughtful review @vashworth; I believe I've addressed all the comments. Let me know if you have any other suggestions. Thank you! |
|
@okorohelijah Resolved 👍🏻 |
| return ignoredSchemes; | ||
| } | ||
|
|
||
| /// Returns scheme names contributed by resolved Swift package checkouts. |
There was a problem hiding this comment.
are "transitive SwiftPM dependencies" covered by this helper? Maybe point it out in the comment?
Spell out that checkout-scheme filtering covers both direct and transitive Swift package checkouts. This addresses review feedback asking whether transitive SwiftPM dependencies are covered by the helper.
|
@lukemmtt Sorry looks like there are merge conflicts again, can you resolve? |
|
For sure, doing it now, thanks! |
…on-swiftpm-scheme-scan # Conflicts: # packages/flutter_tools/lib/src/ios/xcodeproj.dart # packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
|
Resolved 👍🏻 |
|
autosubmit label was removed for flutter/flutter/186006, because This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers and needs 1 more review(s) in order to merge this PR.
|
Summary
In iOS projects with a watchOS companion target and Swift Package Manager enabled,
IosProject.containsWatchCompanion()has a fallback path for modern Xcode projects where the companion relationship is declared throughINFOPLIST_KEY_WKCompanionAppBundleIdentifierinproject.pbxproj, rather than as a literalWKCompanionAppBundleIdentifierkey in the watch targetInfo.plist.That fallback iterates
projectInfo.schemesand runs:for each non-default scheme until it finds the watch companion scheme.
With SwiftPM enabled,
xcodebuild -listcan include schemes for direct Flutter plugin packages and transitive Swift packages, such asadvertising-id,amplitude-flutter,Amplitude-Swift,RevenueCatUI, andSuperwallKit. Those schemes cannot be the host project's watch companion, but each failed watchOS build-settings probe can take multiple seconds.This PR filters those non-host schemes earlier, in
XcodeProjectInterpreter.getInfo(), before they become part ofXcodeProjectInfo.schemes.The current filter excludes:
FlutterGeneratedPluginSwiftPackageFlutterFrameworkxcodeProject.getPlugins()cloud_firestore,cloud-firestore,Cloud-firestorebuild/ios/SourcePackages/checkouts/*/.swiftpm/xcode/xcshareddata/xcschemes/*.xcschemeI also removed the earlier shared-scheme-location filter from
containsWatchCompanion(). That earlier approach was present in 0cfdd3a67c2, but it could skip valid non-shared host schemes. The current approach keeps the fallback behavior scheme-based while removing known SwiftPM package schemes from the candidate list.Fixes #186004.
Measurements
Test project: TimeFinder app.
Command:
Benchmark condition for fallback rows: the literal
WKCompanionAppBundleIdentifierkey was removed from the watch targetInfo.plist, while the modernINFOPLIST_KEY_WKCompanionAppBundleIdentifierentries remained inproject.pbxproj.In this project, Xcode listed 67 schemes. The current filter reduced that to 6 retained schemes, and the fallback reached
TimeFinderWatchAppafter probing only:LiveActivityWidgetExtensionScheduleWidgetExtensionTimeFinderWatchAppIt did not probe direct plugin schemes or transitive SwiftPM package schemes, including
Patrol,Amplitude-Swift,RevenueCatUI,RevenueCatUITests, orSuperwallKit.Worth noting: TimeFinder is a worst-case-ish benchmark project for this issue: it has many SwiftPM package schemes, and
xcodebuild -listreturns schemes alphabetically here, soTimeFinderWatchAppis near the end of the candidate sequence. A project with a watch scheme that sorts early, such asAAAWatchApp, would see far less baseline overhead and therefore less benefit from this PR.Test Plan
flutter test test/general.shard/ios/xcodeproj_test.dartflutter test test/general.shard/project_test.dart --plain-name 'watch companion'flutter analyzeinpackages/flutter_toolsgit diff --check