Skip to content

Commit 29caa97

Browse files
committed
feat: add app icon to WebDriverAgentRunner
Previously the WDA Runner installed as a blank/white icon on iOS home screens because Xcode's auto-generated XCTRunner host app does not inherit icons from the test bundle's asset catalog. This change: - Adds an Appium-logo AppIcon asset catalog to WebDriverAgentRunner so actool compiles iOS icon variants into the .xctest bundle. - Adds Scripts/embed-runner-icon.sh, a scheme post-action that lifts the compiled icons from PlugIns/<name>.xctest/ into the Runner.app root, patches the Runner.app Info.plist with CFBundleIcons / CFBundleIcons~ipad, and re-codesigns the bundle. - Wires the script in via a <PostActions> entry on the scheme's BuildAction so it runs after Xcode's CopyPlistFile step (a Run Script build phase fires too early and gets overwritten). iOS only; tvOS uses a different layered Brand Assets format and is intentionally not handled here.
1 parent daad857 commit 29caa97

6 files changed

Lines changed: 122 additions & 0 deletions

File tree

Scripts/embed-runner-icon.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/bin/bash
2+
# Embed the WDA app icon into the wrapping XCTRunner host app so the
3+
# installed WebDriverAgent shows the Appium logo on the iOS home screen
4+
# instead of a blank icon.
5+
#
6+
# Apple's USES_XCTRUNNER auto-generates a Runner.app around UI-testing
7+
# .xctest bundles but does not inherit icons from the test bundle's
8+
# asset catalog. actool produces AppIcon*.png + Assets.car inside
9+
# PlugIns/<product>.xctest/ where iOS never looks. This script lifts
10+
# them up to the Runner.app root and patches Info.plist accordingly.
11+
#
12+
# Limitations:
13+
# - Touches XCTRunner internals; may need updates across Xcode versions.
14+
# - iOS only; tvOS uses different "Brand Assets" and is not handled.
15+
# - Cloud device farms that re-sign WDA must preserve these changes.
16+
17+
set -euo pipefail
18+
19+
RUNNER_APP="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}-Runner.app"
20+
XCTEST="${RUNNER_APP}/PlugIns/${PRODUCT_NAME}.xctest"
21+
22+
if [ ! -d "$RUNNER_APP" ]; then
23+
echo "warning: ${PRODUCT_NAME}-Runner.app not found at $RUNNER_APP; skipping icon embed"
24+
exit 0
25+
fi
26+
27+
if [ ! -d "$XCTEST" ]; then
28+
echo "warning: ${PRODUCT_NAME}.xctest not found inside Runner.app; skipping icon embed"
29+
exit 0
30+
fi
31+
32+
shopt -s nullglob
33+
ICONS=("$XCTEST"/AppIcon*.png)
34+
if [ ${#ICONS[@]} -eq 0 ]; then
35+
echo "warning: no compiled AppIcon*.png found inside $XCTEST; skipping icon embed"
36+
exit 0
37+
fi
38+
39+
cp -f "${ICONS[@]}" "$RUNNER_APP/"
40+
if [ -f "$XCTEST/Assets.car" ]; then
41+
cp -f "$XCTEST/Assets.car" "$RUNNER_APP/"
42+
fi
43+
44+
PLIST="$RUNNER_APP/Info.plist"
45+
/usr/libexec/PlistBuddy -c "Delete :CFBundleIcons" "$PLIST" 2>/dev/null || true
46+
/usr/libexec/PlistBuddy -c "Delete :CFBundleIcons~ipad" "$PLIST" 2>/dev/null || true
47+
48+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons dict" "$PLIST"
49+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons:CFBundlePrimaryIcon dict" "$PLIST"
50+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons:CFBundlePrimaryIcon:CFBundleIconName string AppIcon" "$PLIST"
51+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons:CFBundlePrimaryIcon:CFBundleIconFiles array" "$PLIST"
52+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons:CFBundlePrimaryIcon:CFBundleIconFiles:0 string AppIcon60x60" "$PLIST"
53+
54+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons~ipad dict" "$PLIST"
55+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons~ipad:CFBundlePrimaryIcon dict" "$PLIST"
56+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons~ipad:CFBundlePrimaryIcon:CFBundleIconName string AppIcon" "$PLIST"
57+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons~ipad:CFBundlePrimaryIcon:CFBundleIconFiles array" "$PLIST"
58+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons~ipad:CFBundlePrimaryIcon:CFBundleIconFiles:0 string AppIcon60x60" "$PLIST"
59+
/usr/libexec/PlistBuddy -c "Add :CFBundleIcons~ipad:CFBundlePrimaryIcon:CFBundleIconFiles:1 string AppIcon76x76" "$PLIST"
60+
61+
# Re-codesign since we modified the bundle after Xcode signed it.
62+
# In a scheme post-action context Xcode's CODE_SIGN_* env vars are not exposed,
63+
# so discover the existing signing identity from the already-signed bundle.
64+
if [ -d "$RUNNER_APP/_CodeSignature" ]; then
65+
EXISTING_IDENT="${EXPANDED_CODE_SIGN_IDENTITY:-}"
66+
if [ -z "$EXISTING_IDENT" ]; then
67+
EXISTING_IDENT=$(codesign -dvv "$RUNNER_APP" 2>&1 \
68+
| awk -F'=' '/^Authority/ {print $2; exit}')
69+
fi
70+
if [ -n "$EXISTING_IDENT" ]; then
71+
codesign --force --sign "$EXISTING_IDENT" \
72+
--preserve-metadata=identifier,entitlements "$RUNNER_APP"
73+
else
74+
echo "warning: bundle is signed but no identity discovered; signature will be invalid"
75+
fi
76+
fi
77+
78+
echo "embedded icon into $RUNNER_APP"

WebDriverAgent.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@
826826
F59CD6D52EF16E5E00F91287 /* XCUIElement+FBCustomActions.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CD6D32EF16E5E00F91287 /* XCUIElement+FBCustomActions.m */; };
827827
F59CD6D62EF16E5E00F91287 /* XCUIElement+FBCustomActions.h in Headers */ = {isa = PBXBuildFile; fileRef = F59CD6D22EF16E5E00F91287 /* XCUIElement+FBCustomActions.h */; };
828828
F59CD6D72EF16E5E00F91287 /* XCUIElement+FBCustomActions.m in Sources */ = {isa = PBXBuildFile; fileRef = F59CD6D32EF16E5E00F91287 /* XCUIElement+FBCustomActions.m */; };
829+
A1B2C3D4E5F600000000001B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F600000000001A /* Assets.xcassets */; };
829830
/* End PBXBuildFile section */
830831

831832
/* Begin PBXContainerItemProxy section */
@@ -1368,6 +1369,7 @@
13681369
EE9AB7921CAEDF0C008C271F /* FBRuntimeUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBRuntimeUtils.m; sourceTree = "<group>"; };
13691370
EE9AB7FC1CAEE048008C271F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = WebDriverAgentRunner/Info.plist; sourceTree = SOURCE_ROOT; };
13701371
EE9AB7FD1CAEE048008C271F /* UITestingUITests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UITestingUITests.m; path = WebDriverAgentRunner/UITestingUITests.m; sourceTree = SOURCE_ROOT; };
1372+
A1B2C3D4E5F600000000001A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = WebDriverAgentRunner/Assets.xcassets; sourceTree = SOURCE_ROOT; };
13711373
EE9AB8031CAEE182008C271F /* build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build.sh; sourceTree = "<group>"; };
13721374
EE9B75D41CF7956C00275851 /* IntegrationApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IntegrationApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
13731375
EE9B75EC1CF7956C00275851 /* IntegrationTests_1.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntegrationTests_1.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -2294,6 +2296,7 @@
22942296
children = (
22952297
EE9AB7FC1CAEE048008C271F /* Info.plist */,
22962298
EE9AB7FD1CAEE048008C271F /* UITestingUITests.m */,
2299+
A1B2C3D4E5F600000000001A /* Assets.xcassets */,
22972300
);
22982301
name = WebDriverAgentRunner;
22992302
path = XCTUITestRunner;
@@ -3110,6 +3113,7 @@
31103113
isa = PBXResourcesBuildPhase;
31113114
buildActionMask = 2147483647;
31123115
files = (
3116+
A1B2C3D4E5F600000000001B /* Assets.xcassets in Resources */,
31133117
);
31143118
runOnlyForDeploymentPostprocessing = 0;
31153119
};
@@ -4346,6 +4350,7 @@
43464350
isa = XCBuildConfiguration;
43474351
baseConfigurationReference = EEE5CABF1C80361500CBBDD9 /* IOSSettings.xcconfig */;
43484352
buildSettings = {
4353+
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
43494354
CLANG_STATIC_ANALYZER_MODE = deep;
43504355
DEBUG_INFORMATION_FORMAT = dwarf;
43514356
ENABLE_TESTING_SEARCH_PATHS = YES;
@@ -4399,6 +4404,7 @@
43994404
isa = XCBuildConfiguration;
44004405
baseConfigurationReference = EEE5CABF1C80361500CBBDD9 /* IOSSettings.xcconfig */;
44014406
buildSettings = {
4407+
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
44024408
CLANG_STATIC_ANALYZER_MODE = deep;
44034409
ENABLE_TESTING_SEARCH_PATHS = YES;
44044410
FRAMEWORK_SEARCH_PATHS = "$(inherited)";

WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@
55
<BuildAction
66
parallelizeBuildables = "YES"
77
buildImplicitDependencies = "YES">
8+
<PostActions>
9+
<ExecutionAction
10+
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
11+
<ActionContent
12+
title = "Embed app icon into Runner.app"
13+
scriptText = "&quot;${SRCROOT}/Scripts/embed-runner-icon.sh&quot;&#10;">
14+
<EnvironmentBuildable>
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "EEF988291C486603005CA669"
18+
BuildableName = "WebDriverAgentRunner.xctest"
19+
BlueprintName = "WebDriverAgentRunner"
20+
ReferencedContainer = "container:WebDriverAgent.xcodeproj">
21+
</BuildableReference>
22+
</EnvironmentBuildable>
23+
</ActionContent>
24+
</ExecutionAction>
25+
</PostActions>
826
<BuildActionEntries>
927
<BuildActionEntry
1028
buildForTesting = "YES"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "icon-1024.png",
5+
"idiom" : "universal",
6+
"platform" : "ios",
7+
"size" : "1024x1024"
8+
}
9+
],
10+
"info" : {
11+
"author" : "xcode",
12+
"version" : 1
13+
}
14+
}
62 KB
Loading
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}

0 commit comments

Comments
 (0)