Skip to content

Commit 3a28b6e

Browse files
Fix flutter#160622: change containsWatchConpanion function to detect companion watch apps defined by only the project info file. (flutter#176832)
(Please be generous for some typos and grammar error in sentences below. English is not my mother tongue) TRDR : This patch fixes flutter run(and might be flutter build too) failure on iOS flutter apps with companion watchOS app in case the WKCompanionAppBundleIdentifier value is defined by xcode project configuration and uses different app bundle id by scheme(which happens when you do something like com.myapp.app1.dev things for scheme bundle id) This change checks default scheme (the scheme with corresponding name for the debug/release mode and selected build flavor.) in the build settings check step of containsWatchCompanion function. current code of containsWatchCompanion function works like below 1. check all default Info.plist file's content of all targets to determine if the project has watchOS companion apps. (this doesn't work well in mordern xcode settings when they use multi plist files which are selected on build time by build configuration. Sometimes, the default Info.plist file doesn't even exist in the project.) 2. check if "WKCompanionAppBundleIdentifier" is included in the xcode project info file(the ios/Runner.xcodeproj/project.pbxproj file in case of iOS project generated by flutter) 3. If "WKCompanionAppBundleIdentifier" has found in the project info file, check build configuration variables of every single scheme but the current target scheme if they have config value with key "INFOPLIST_KEY_WKCompanionAppBundleIdentifier" identical to current build configuration's bundle identifier returned by productBundleIdentifier@xcode_project.dart function. I believe The third step causes many problems reported in some issue and pr, saying iOS app with companion watchOS app fails on build or run. flutter#160622 flutter#172436 In my case, My iOS project had multiple schemes which are linked to different build configurations, but using single Runner setting per app. because we exclude current scheme for the check, we get all the INFOPLIST_KEY_WKCompanionAppBundleIdentifier values except the one which should be same with the identifier returned by productBundleIdentifier@xcode_project.dart function. that is because companion watch apps doesn't have to have same WKCompanionAppBundleIdentifier with other scheme's main app. We have to check the default scheme to see watchOS companion app`s WKCompanionAppBundleIdentifier, not other scheme. (other schemes can have slightly different or totally different bundle id. that was to support different environment systems like dev, prod in my case) as a result, flutter run gave me WatchOS app built for device target iOS/iPad, not Watch. which caused problem during installation of iOS app to a simulator because the embeded Watch App was in invalid format. I found this by inspecting Info.plist of generated watchOS app. below is part of the flutter run verbose log with failure caused by old code. [+1154 ms] An error was encountered processing the command (domain=IXUserPresentableErrorDomain, code=1): App installation failed: ‘Bora Debug-dev’을(를) 설치할 수 없음 (which says "cannot install" in korean) 나중에 다시 시도하십시오. (which says "try again later" in korean) Found WatchKit 2.0 app at /Users/javalia/Library/Developer/CoreSimulator/Devices/071691B3-7901-47E5-9B38-4D5B799F3530/data/Library/Caches/com.apple.mobile.installd.staging/temp.8HZIEG/extracted/Payload/Runner.app/Watch/bora Watch App.app but it does not have a WKWatchKitApp or WKApplication key set to true in its Info.plist Underlying error (domain=IXUserPresentableErrorDomain, code=1): ‘Bora Debug-dev’을(를) 설치할 수 없음 (which says "cannot install" in korean) 나중에 다시 시도하십시오. (which says "try it later" in korean) I suggest this change with two reasons : 1. I think forcing watchOS app makers split their runner configurations to support watch companion app is something not recommendable/not good to force as a convention. (Which is implied in the comment which is removed by this pr, and could be one of the valid detouring. some people in this issue flutter#160622 said that a detouring is adding of dummy WKCompanionAppBundleIdentifier configuration in iOS app, which should be in only watchOS apps in normal.) 2. The function should work as it's name implies, so I guess it should not omit default scheme during check. Matching function's behavior with it's name will be likely to reduce potential errors caused by this change while fixing problems. ## Pre-launch Checklist - [ X ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ X ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ X ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ X ] I signed the [CLA]. - [ X ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com>
1 parent eeef182 commit 3a28b6e

File tree

2 files changed

+11
-20
lines changed

2 files changed

+11
-20
lines changed

packages/flutter_tools/lib/src/xcode_project.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -796,13 +796,13 @@ def __lldb_init_module(debugger: lldb.SBDebugger, _):
796796
projectInfo.reportFlavorNotFoundAndExit();
797797
}
798798
for (final String scheme in projectInfo.schemes) {
799-
// the default scheme should not be a watch scheme, so skip it
799+
// Flutter assumes single build target per scheme, so skip default scheme.
800800
if (scheme == defaultScheme) {
801801
continue;
802802
}
803+
803804
final Map<String, String>? allBuildSettings = await buildSettingsForBuildInfo(
804805
buildInfo,
805-
deviceId: deviceId,
806806
scheme: scheme,
807807
isWatch: true,
808808
);

packages/flutter_tools/test/general.shard/project_test.dart

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,10 @@ resolution: workspace
18901890
testPlistParser = FakePlistParser();
18911891
mockXcodeProjectInterpreter = FakeXcodeProjectInterpreter();
18921892
flutterProjectFactory = FlutterProjectFactory(fileSystem: fs, logger: logger);
1893+
const buildContext = XcodeProjectBuildContext(scheme: 'Runner');
1894+
mockXcodeProjectInterpreter.buildSettingsByBuildContext[buildContext] = <String, String>{
1895+
IosProject.kProductBundleIdKey: 'io.flutter.someProject',
1896+
};
18931897
});
18941898

18951899
testUsingContext(
@@ -1920,12 +1924,8 @@ resolution: workspace
19201924
},
19211925
);
19221926

1923-
group('with bundle identifier', () {
1927+
group('with bundle identifier and multiple schemes', () {
19241928
setUp(() {
1925-
const buildContext = XcodeProjectBuildContext(scheme: 'Runner');
1926-
mockXcodeProjectInterpreter.buildSettingsByBuildContext[buildContext] = <String, String>{
1927-
IosProject.kProductBundleIdKey: 'io.flutter.someProject',
1928-
};
19291929
mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo(
19301930
<String>['Runner', 'WatchTarget'],
19311931
<String>[],
@@ -1991,7 +1991,6 @@ resolution: workspace
19911991
.childDirectory('WatchTarget')
19921992
.childFile('Info.plist')
19931993
.createSync(recursive: true);
1994-
19951994
testPlistParser.setProperty(
19961995
'WKCompanionAppBundleIdentifier',
19971996
'io.flutter.someOTHERproject',
@@ -2048,7 +2047,10 @@ resolution: workspace
20482047
() async {
20492048
final FlutterProject project = await someProject();
20502049
project.ios.xcodeProject.createSync();
2051-
const buildContext = XcodeProjectBuildContext(scheme: 'Runner', deviceId: '123');
2050+
const buildContext = XcodeProjectBuildContext(
2051+
scheme: 'Runner',
2052+
deviceId: '123',
2053+
); // for substituteXcodeVariables call of plist parsing
20522054
mockXcodeProjectInterpreter.buildSettingsByBuildContext[buildContext] = <String, String>{
20532055
IosProject.kProductBundleIdKey: 'io.flutter.someProject',
20542056
};
@@ -2089,14 +2091,8 @@ resolution: workspace
20892091
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = io.flutter.someProject
20902092
''');
20912093

2092-
const buildContext = XcodeProjectBuildContext(scheme: 'Runner', deviceId: '123');
2093-
mockXcodeProjectInterpreter.buildSettingsByBuildContext[buildContext] = <String, String>{
2094-
IosProject.kProductBundleIdKey: 'io.flutter.someProject',
2095-
};
2096-
20972094
const watchBuildContext = XcodeProjectBuildContext(
20982095
scheme: 'WatchScheme',
2099-
deviceId: '123',
21002096
sdk: XcodeSdk.WatchOS,
21012097
);
21022098
mockXcodeProjectInterpreter.buildSettingsByBuildContext[watchBuildContext] =
@@ -2131,14 +2127,9 @@ resolution: workspace
21312127
Build settings for action build and target "WatchTarget":
21322128
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = $(PRODUCT_BUNDLE_IDENTIFIER)
21332129
''');
2134-
const buildContext = XcodeProjectBuildContext(scheme: 'Runner', deviceId: '123');
2135-
mockXcodeProjectInterpreter.buildSettingsByBuildContext[buildContext] = <String, String>{
2136-
IosProject.kProductBundleIdKey: 'io.flutter.someProject',
2137-
};
21382130

21392131
const watchBuildContext = XcodeProjectBuildContext(
21402132
scheme: 'WatchScheme',
2141-
deviceId: '123',
21422133
sdk: XcodeSdk.WatchOS,
21432134
);
21442135
mockXcodeProjectInterpreter.buildSettingsByBuildContext[watchBuildContext] =

0 commit comments

Comments
 (0)