fix(ios): keep App Group entitlement on widget/share extensions in App Store builds (#4891)

The App Store export re-sign (xcodebuild -exportArchive, automatic signing)
stripped com.apple.security.application-groups from the ReadestWidget and
ShareExtension binaries because both targets set
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION: YES. The provisioning profiles and
source entitlements both grant the group, but the signed extension binaries did
not, so the widget read an empty snapshot from the shared App Group container
and showed only the placeholder book icon. Dev builds were unaffected.

Remove the flag from both extension targets (the main app already ships the
group correctly without it) so signing uses the exact CODE_SIGN_ENTITLEMENTS
content. Add scripts/verify-ios-appstore-entitlements.sh and run it from
release-ios-appstore.sh before upload so a stripped App Group fails the release
instead of shipping a dead widget.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-07-02 23:22:26 +09:00
committed by GitHub
parent a3609731c3
commit 81802a7c72
3 changed files with 67 additions and 11 deletions
@@ -3,6 +3,10 @@ pnpm tauri ios build --export-method app-store-connect
BUNDLE_DIR=src-tauri/gen/apple/build/arm64
IPA_BUNDLE=$BUNDLE_DIR/Readest.ipa
# Guard: the App Store export re-sign must not strip the App Group entitlement
# from the widget / share extensions, or the reading widget ships dead.
bash scripts/verify-ios-appstore-entitlements.sh "$IPA_BUNDLE"
xcrun altool --upload-app --type ios --file $IPA_BUNDLE --apiKey $APPLE_API_KEY --apiIssuer $APPLE_API_ISSUER
echo "iOS build uploaded to App Store Connect."
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Verify the App Store IPA's app extensions carry the App Group entitlement.
#
# group.com.bilingify.readest is how the main app hands the reading-widget
# snapshot (and the share extension its shared state) to its extensions via the
# shared App Group container. Automatic App Store signing re-signs embedded
# extensions during `xcodebuild -exportArchive`; if a target sets
# CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION=YES, that re-sign silently strips
# com.apple.security.application-groups from the extension binary even though
# the provisioning profile and source .entitlements both grant it. The widget
# then reads an empty snapshot and shows only a placeholder book icon, with no
# build error. This guard fails the release before upload so it can't ship
# broken again.
set -euo pipefail
IPA="${1:-src-tauri/gen/apple/build/arm64/Readest.ipa}"
GROUP="group.com.bilingify.readest"
EXTS=(ReadestWidget ShareExtension)
if [ ! -f "$IPA" ]; then
echo "verify-ios-appstore-entitlements: IPA not found at $IPA" >&2
exit 1
fi
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
unzip -q "$IPA" -d "$WORK"
APP="$WORK/Payload/Readest.app"
fail=0
for ext in "${EXTS[@]}"; do
appex="$APP/PlugIns/$ext.appex"
if codesign -d --entitlements :- "$appex" 2>/dev/null | grep -q "$GROUP"; then
echo "OK $ext.appex carries $GROUP"
else
echo "FAIL $ext.appex is MISSING $GROUP (App Group access broken)"
fail=1
fi
done
if [ "$fail" -ne 0 ]; then
echo "" >&2
echo "App Group entitlement missing from an extension binary. Do NOT set" >&2
echo "CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION on extension targets in" >&2
echo "src-tauri/gen/apple/project.yml." >&2
exit 1
fi
echo "All app extensions carry the App Group entitlement."
@@ -153,16 +153,16 @@ targets:
PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest.ShareExtension
DEVELOPMENT_TEAM: J5W48D69VR
CODE_SIGN_ENTITLEMENTS: ShareExtension/ShareExtension.entitlements
# Permit Xcode's productPackagingUtility to write the auto-derived
# signing entitlements (application-identifier, team-identifier,
# get-task-allow) back to the source entitlements file at build
# time. Without this, Xcode 16+ refuses the modification it
# itself just performed and the build fails with:
# "Entitlements file ... was modified during the build"
# Safe for this target because the entitlements file is empty —
# the only modifications Xcode performs are the team / app-id
# auto-population every iOS extension target needs anyway.
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION: YES
# Do NOT set CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION here. This
# entitlements file declares com.apple.security.application-groups
# (group.com.bilingify.readest). With automatic App Store signing,
# that flag lets `xcodebuild -exportArchive` overwrite the extension
# entitlements with a minimal auto-derived set that DROPS the app
# group — silently breaking App Group access in App Store builds
# (dev builds are unaffected). The main app ships the group
# correctly without this flag. If Xcode ever reports "Entitlements
# file was modified during the build", fix it by adding the
# auto-derived keys to the source file, not by re-enabling this flag.
SWIFT_VERSION: "5.0"
IPHONEOS_DEPLOYMENT_TARGET: 15.0
ARCHS: [arm64]
@@ -192,7 +192,10 @@ targets:
PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest.ReadestWidget
DEVELOPMENT_TEAM: J5W48D69VR
CODE_SIGN_ENTITLEMENTS: ReadestWidget/ReadestWidget.entitlements
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION: YES
# CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION is intentionally omitted
# (see ShareExtension): with it, the App Store export re-sign strips
# com.apple.security.application-groups, so the widget reads an empty
# snapshot from the App Group and shows only the placeholder book icon.
SWIFT_VERSION: "5.0"
IPHONEOS_DEPLOYMENT_TARGET: 15.0
ARCHS: [arm64]