Files
readest/apps/readest-app/src-tauri/gen/apple/project.yml
T
Huang Xin 81802a7c72 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>
2026-07-02 16:22:26 +02:00

209 lines
9.0 KiB
YAML

name: Readest
options:
bundleIdPrefix: com.bilingify.readest
deploymentTarget:
iOS: 15.0
fileGroups: [../../src]
configs:
debug: debug
release: release
settingGroups:
app:
base:
PRODUCT_NAME: Readest
PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest
DEVELOPMENT_TEAM: J5W48D69VR
targetTemplates:
app:
type: application
sources:
- path: Sources
scheme:
environmentVariables:
RUST_BACKTRACE: full
RUST_LOG: info
settings:
groups: [app]
targets:
Readest_iOS:
type: application
platform: iOS
# NOTE on In-App Purchase capability
# ----------------------------------
# IAP is enabled at three layers:
# 1. App ID `com.bilingify.readest` services (Apple Developer
# portal) — must have "In-App Purchase" service ON.
# 2. Provisioning profile — pulled automatically by Xcode when
# DEVELOPMENT_TEAM is set; inherits IAP from the App ID.
# 3. StoreKit framework — linked transitively by the
# tauri-plugin-native-bridge plugin's iap_* commands.
# The `SystemCapabilities` pbxproj entry that Xcode writes when you
# add IAP via Signing & Capabilities is informational only; it
# doesn't gate the actual capability. xcodegen 2.45 can't
# round-trip nested dictionaries into pbxproj attributes (it
# serializes them as quoted strings), so we omit the entry rather
# than ship a malformed one. IAP works without it.
sources:
- path: Sources
- path: Assets.xcassets
# Only reference Externals as a file group (no compile/resource
# phases) — the actual libapp.a lookup happens via the
# `framework: libapp.a` dependency + LIBRARY_SEARCH_PATHS pointing
# at `Externals/arm64/$(CONFIGURATION)`. Listing the whole folder
# as a source caused xcodegen to add copy-resource phases for
# both `debug/libapp.a` and `release/libapp.a`, conflicting at
# build time:
# "Multiple commands produce Readest.app/libapp.a"
- path: Externals
buildPhase: none
- path: Readest_iOS
- path: assets
buildPhase: resources
type: folder
- path: LaunchScreen.storyboard
# Info.plist and entitlements live on disk with hand-tuned content
# (CFBundleDocumentTypes, UTExportedTypeDeclarations, locales,
# CFBundleURLTypes for readest://, com.apple.developer.applesignin,
# associated-domains for Universal Links). Reference them via
# INFOPLIST_FILE / CODE_SIGN_ENTITLEMENTS instead of `info:` /
# `entitlements:` so xcodegen treats them as opaque inputs and
# doesn't rewrite them on regeneration.
scheme:
environmentVariables:
RUST_BACKTRACE: full
RUST_LOG: info
settings:
base:
INFOPLIST_FILE: Readest_iOS/Info.plist
CODE_SIGN_ENTITLEMENTS: Readest_iOS/Readest_iOS.entitlements
ENABLE_BITCODE: false
ARCHS: [arm64]
VALID_ARCHS: arm64
LIBRARY_SEARCH_PATHS[arch=x86_64]: $(inherited) $(PROJECT_DIR)/Externals/x86_64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
LIBRARY_SEARCH_PATHS[arch=arm64]: $(inherited) $(PROJECT_DIR)/Externals/arm64/$(CONFIGURATION) $(SDKROOT)/usr/lib/swift $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME) $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true
# Tell Xcode the app embeds Swift code (the new ShareExtension
# is a Swift app-extension target). Without this the
# swift-stdlib-tool phase that copies libswiftCore.dylib +
# friends into Readest.app/Frameworks runs incorrectly under
# Xcode 26 — the app launches and immediately dyld-fails with
# "libswiftCore.dylib: no such file".
EMBEDDED_CONTENT_CONTAINS_SWIFT_CODE: YES
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64
groups: [app]
dependencies:
- framework: libapp.a
embed: false
- target: ShareExtension
embed: true
codeSign: true
- target: ReadestWidget
embed: true
codeSign: true
- sdk: CoreGraphics.framework
- sdk: Metal.framework
- sdk: MetalKit.framework
- sdk: QuartzCore.framework
- sdk: Security.framework
- sdk: UIKit.framework
- sdk: WebKit.framework
- sdk: WidgetKit.framework
preBuildScripts:
- script: pnpm tauri ios xcode-script -v --platform ${PLATFORM_DISPLAY_NAME:?} --sdk-root ${SDKROOT:?} --framework-search-paths "${FRAMEWORK_SEARCH_PATHS:?}" --header-search-paths "${HEADER_SEARCH_PATHS:?}" --gcc-preprocessor-definitions "${GCC_PREPROCESSOR_DEFINITIONS:-}" --configuration ${CONFIGURATION:?} ${FORCE_COLOR} ${ARCHS:?}
name: Build Rust Code
basedOnDependencyAnalysis: false
outputFiles:
- $(SRCROOT)/Externals/x86_64/${CONFIGURATION}/libapp.a
- $(SRCROOT)/Externals/arm64/${CONFIGURATION}/libapp.a
# Share-sheet extension that lets users send article URLs from Safari /
# Chrome / etc. directly to Readest. The extension grabs the URL via
# NSExtensionContext, encodes it into `readest://share?url=...`, and
# opens the main app — which catches the deep-link via
# `tauri-plugin-deep-link` and runs the clip-and-import pipeline.
ShareExtension:
type: app-extension
platform: iOS
# The Share Extension uses iOS-system-localized bar button items
# (`barButtonSystemItem: .cancel` / `.save`) plus a JS-supplied
# "Default" group name (passed through the App Group), so the target
# carries no per-locale `.strings` files — no `.lproj` directories
# to wire up.
sources:
- path: ShareExtension
info:
path: ShareExtension/Info.plist
properties:
CFBundleDisplayName: Readest
NSExtension:
NSExtensionPointIdentifier: com.apple.share-services
NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).ShareViewController
NSExtensionAttributes:
NSExtensionActivationRule:
NSExtensionActivationSupportsWebURLWithMaxCount: 1
NSExtensionActivationSupportsText: true
# Entitlements referenced via CODE_SIGN_ENTITLEMENTS build setting
# rather than xcodegen's `entitlements:` block — otherwise xcodegen
# may overwrite the file (even with no properties declared) during
# regeneration, and Xcode rejects the build with
# "Entitlements file ... was modified during the build"
settings:
base:
PRODUCT_NAME: ShareExtension
PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest.ShareExtension
DEVELOPMENT_TEAM: J5W48D69VR
CODE_SIGN_ENTITLEMENTS: ShareExtension/ShareExtension.entitlements
# 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]
VALID_ARCHS: arm64
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64
ENABLE_BITCODE: false
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: false
SKIP_INSTALL: true
# WidgetKit extension that surfaces the user's current reading progress
# on the iOS home screen / Today view. Reads from the App Group suite
# written by the main app's useReadingWidget hook (Task 7).
ReadestWidget:
type: app-extension
platform: iOS
sources:
- path: ReadestWidget
info:
path: ReadestWidget/Info.plist
properties:
CFBundleDisplayName: Readest
NSExtension:
NSExtensionPointIdentifier: com.apple.widgetkit-extension
settings:
base:
PRODUCT_NAME: ReadestWidget
PRODUCT_BUNDLE_IDENTIFIER: com.bilingify.readest.ReadestWidget
DEVELOPMENT_TEAM: J5W48D69VR
CODE_SIGN_ENTITLEMENTS: ReadestWidget/ReadestWidget.entitlements
# 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]
VALID_ARCHS: arm64
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64
ENABLE_BITCODE: false
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: false
SKIP_INSTALL: true
dependencies:
- sdk: WidgetKit.framework