forked from akai/readest
1d3dfd395f
The iOS share extension is a web-article URL clipper, but its activation rule enabled NSExtensionActivationSupportsText. A .txt file is public.plain-text (conforms to public.text), so that key made the URL-only extension activate for plain-text files it cannot handle: the share sheet hung instead of the file taking the main app's CFBundleDocumentTypes "Copy to Readest" import path, which handles txt fine (like EPUB and PDF, which never matched the extension). Drop NSExtensionActivationSupportsText so the extension activates only for web URLs. Shared .txt files now route to the working document-open import path; sharing a web page URL from Safari or Chrome still works. Add a regression guard asserting project.yml (the xcodegen source of truth for the generated, skip-worktree Info.plist) never re-enables text activation. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
220 lines
9.5 KiB
YAML
220 lines
9.5 KiB
YAML
name: Readest
|
|
options:
|
|
bundleIdPrefix: com.bilingify.readest
|
|
deploymentTarget:
|
|
iOS: 15.0
|
|
fileGroups: [../../src]
|
|
packages:
|
|
Sentry:
|
|
url: https://github.com/getsentry/sentry-cocoa
|
|
from: 8.0.0
|
|
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: SentrySupport
|
|
- 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:
|
|
- package: Sentry
|
|
product: Sentry
|
|
- 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:
|
|
# Web URLs only. Do NOT add NSExtensionActivationSupportsText: a
|
|
# .txt file is public.plain-text (conforms to public.text), so that
|
|
# key makes this article-URL clipper activate for plain-text FILES
|
|
# it can't handle (the share sheet hangs). Book files must fall
|
|
# through to the main app's CFBundleDocumentTypes import path.
|
|
NSExtensionActivationRule:
|
|
NSExtensionActivationSupportsWebURLWithMaxCount: 1
|
|
# 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
|