Fix empty CFBundleVersion app-extension install failure via committed xcconfig fallbacks
Co-authored-by: premnirmal <1255689+premnirmal@users.noreply.github.com>
This commit is contained in:
co-authored by
premnirmal
parent
8a74ad2428
commit
4330dc06e2
@@ -62,12 +62,13 @@ jobs:
|
||||
run: brew install xcodegen
|
||||
- name: Generate Xcode project
|
||||
working-directory: iosApp
|
||||
# Source version.sh first so XcodeGen can expand ${MARKETING_VERSION} /
|
||||
# ${CURRENT_PROJECT_VERSION} in project.yml from the latest git tag
|
||||
# (mirroring the Android version derivation). fetch-depth: 0 above brings
|
||||
# Run version.sh first so the git-derived MARKETING_VERSION /
|
||||
# CURRENT_PROJECT_VERSION are written to Version.local.xcconfig (included
|
||||
# by the committed Version.xcconfig) and stamped into the bundles,
|
||||
# mirroring the Android version derivation. fetch-depth: 0 above brings
|
||||
# the tags this needs.
|
||||
run: |
|
||||
source ./version.sh
|
||||
./version.sh
|
||||
xcodegen generate
|
||||
- name: Install iOS platform
|
||||
# The macos-26 image ships the iOS 26 SDK but not the iOS 26 platform
|
||||
|
||||
@@ -31,6 +31,9 @@ DerivedData/
|
||||
.swiftpm/
|
||||
# Local, machine-specific JAVA_HOME override for the iOS Gradle build phase
|
||||
iosApp/.xcode.env.local
|
||||
# Local, git-derived version override written by iosApp/version.sh (optionally
|
||||
# #included by the committed iosApp/Version.xcconfig)
|
||||
iosApp/Version.local.xcconfig
|
||||
#iOS Firebase config (optional, prod only)
|
||||
iosApp/GoogleService-Info.plist
|
||||
iosApp/iosApp/GoogleService-Info.plist
|
||||
|
||||
+11
-8
@@ -88,16 +88,19 @@ On a Mac:
|
||||
2. Generate the project:
|
||||
```sh
|
||||
cd iosApp
|
||||
source ./version.sh # derives the version from the latest git tag
|
||||
./version.sh # stamps the version from the latest git tag
|
||||
xcodegen generate # produces iosApp/StockTicker.xcodeproj
|
||||
```
|
||||
`version.sh` exports `MARKETING_VERSION` (`CFBundleShortVersionString`) and
|
||||
`CURRENT_PROJECT_VERSION` (`CFBundleVersion`) from `git describe --tags`,
|
||||
mirroring how the Android app derives its `versionName` / `versionCode` in
|
||||
`app/build.gradle.kts` (falling back to `1.0` / `1` when no tag is
|
||||
reachable). `project.yml` references those env vars, so source it before
|
||||
generating — otherwise the bundle version is empty and an app-extension
|
||||
install fails.
|
||||
`version.sh` writes `MARKETING_VERSION` (`CFBundleShortVersionString`) and
|
||||
`CURRENT_PROJECT_VERSION` (`CFBundleVersion`) into the git-ignored
|
||||
`Version.local.xcconfig` from `git describe --tags`, mirroring how the Android
|
||||
app derives its `versionName` / `versionCode` in `app/build.gradle.kts`
|
||||
(falling back to `1.0` / `1` when no tag is reachable). The committed
|
||||
`Version.xcconfig` (read by every target and optionally including
|
||||
`Version.local.xcconfig`) already ships non-empty `1.0` / `1` fallbacks, so
|
||||
even if you skip `version.sh` the bundle version is never empty and the
|
||||
app-extension install still succeeds — running `version.sh` just stamps the
|
||||
real git-derived version for TestFlight/App Store archives.
|
||||
3. Open `iosApp/StockTicker.xcodeproj` and run, or build from the command line:
|
||||
```sh
|
||||
xcodebuild build \
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// App/extension version, consumed by every target's Info.plist via
|
||||
// CFBundleShortVersionString ($(MARKETING_VERSION)) and CFBundleVersion
|
||||
// ($(CURRENT_PROJECT_VERSION)).
|
||||
//
|
||||
// These committed values are deliberately NON-EMPTY fallbacks: an app extension
|
||||
// whose CFBundleVersion resolves to an empty string fails to install on the
|
||||
// simulator/device with
|
||||
// "bundleVersion must be set in placeholder attributes for an app extension
|
||||
// placeholder" (IXErrorDomain, code 17).
|
||||
// Because they live in this xcconfig (read by Xcode at build time) rather than
|
||||
// being substituted into the generated project at `xcodegen generate` time,
|
||||
// the build is always installable even if you forget to run `version.sh`.
|
||||
//
|
||||
// To stamp the real, git-derived version (mirroring the Android app), run
|
||||
// `iosApp/version.sh`. It writes `iosApp/Version.local.xcconfig` (git-ignored)
|
||||
// with the values from `git describe --tags`, which the optional include below
|
||||
// layers on top of these defaults — so a proper version is used for
|
||||
// TestFlight/App Store archives without dirtying this committed file.
|
||||
MARKETING_VERSION = 1.0
|
||||
CURRENT_PROJECT_VERSION = 1
|
||||
|
||||
// Optional machine-local override written by version.sh (git-ignored). The `?`
|
||||
// makes the include optional, so builds still succeed when it is absent.
|
||||
#include? "Version.local.xcconfig"
|
||||
+22
-12
@@ -18,6 +18,20 @@ options:
|
||||
iOS: "17.0"
|
||||
createIntermediateGroups: true
|
||||
|
||||
# App/extension version, derived from the latest git tag (mirroring the Android
|
||||
# app — see app/build.gradle.kts). Version.xcconfig sets CFBundleShortVersionString
|
||||
# ($(MARKETING_VERSION)) and CFBundleVersion ($(CURRENT_PROJECT_VERSION)) for
|
||||
# every target, with committed non-empty fallbacks (1.0 / 1). It optionally
|
||||
# #includes Version.local.xcconfig, which `iosApp/version.sh` writes with the
|
||||
# git-derived values. Resolving these in an xcconfig (read by Xcode at build
|
||||
# time) — rather than substituting `${...}` at `xcodegen generate` time — means
|
||||
# the bundle version is NEVER empty even if you forget to run version.sh, so an
|
||||
# app extension no longer fails to install with "bundleVersion must be set in
|
||||
# placeholder attributes for an app extension placeholder" (IXErrorDomain).
|
||||
configFiles:
|
||||
Debug: Version.xcconfig
|
||||
Release: Version.xcconfig
|
||||
|
||||
settings:
|
||||
base:
|
||||
SWIFT_VERSION: "5.0"
|
||||
@@ -27,18 +41,14 @@ settings:
|
||||
# Kotlin crash frames. Debug builds default to plain `dwarf` (no dSYM); forcing dwarf-with-dsym
|
||||
# here ensures symbolication works for Release/Archive (TestFlight/App Store) builds.
|
||||
DEBUG_INFORMATION_FORMAT: "dwarf-with-dsym"
|
||||
# App/extension version, derived from the latest git tag (mirroring the
|
||||
# Android app — see app/build.gradle.kts). These back
|
||||
# CFBundleShortVersionString ($(MARKETING_VERSION)) and CFBundleVersion
|
||||
# ($(CURRENT_PROJECT_VERSION)) in every target's Info.plist. They MUST be
|
||||
# non-empty: an app extension whose CFBundleVersion resolves to an empty
|
||||
# string fails to install with "bundleVersion must be set in placeholder
|
||||
# attributes for an app extension placeholder" (IXErrorDomain). XcodeGen
|
||||
# expands the ${...} references below from the environment, so source
|
||||
# `iosApp/version.sh` before `xcodegen generate` (it derives the values from
|
||||
# `git describe --tags` and falls back to 1.0 / 1 when no tag is reachable).
|
||||
MARKETING_VERSION: "${MARKETING_VERSION}"
|
||||
CURRENT_PROJECT_VERSION: "${CURRENT_PROJECT_VERSION}"
|
||||
# App/extension version comes from Version.xcconfig (see the top-level
|
||||
# `configFiles` above): CFBundleShortVersionString ($(MARKETING_VERSION))
|
||||
# and CFBundleVersion ($(CURRENT_PROJECT_VERSION)). Those MUST be non-empty:
|
||||
# an app extension whose CFBundleVersion resolves to an empty string fails to
|
||||
# install with "bundleVersion must be set in placeholder attributes for an
|
||||
# app extension placeholder" (IXErrorDomain). Version.xcconfig ships
|
||||
# non-empty fallbacks and optionally includes Version.local.xcconfig (written
|
||||
# by iosApp/version.sh) for the git-derived version.
|
||||
# The shared framework is a static Kotlin/Native framework, so it only has
|
||||
# to be on the linker search path — it is not embedded.
|
||||
FRAMEWORK_SEARCH_PATHS:
|
||||
|
||||
+29
-10
@@ -4,21 +4,29 @@
|
||||
# (app/build.gradle.kts derives versionName/versionCode from
|
||||
# `git describe --tags --abbrev=0`).
|
||||
#
|
||||
# Source this script before generating the Xcode project so XcodeGen can expand
|
||||
# the `${MARKETING_VERSION}` / `${CURRENT_PROJECT_VERSION}` references in
|
||||
# project.yml:
|
||||
# Run this before building/generating so the git-derived version is stamped into
|
||||
# the app + widget bundles:
|
||||
#
|
||||
# cd iosApp
|
||||
# source ./version.sh
|
||||
# ./version.sh # writes iosApp/Version.local.xcconfig
|
||||
# xcodegen generate
|
||||
#
|
||||
# It exports two environment variables:
|
||||
# It writes two Xcode build settings into `iosApp/Version.local.xcconfig`
|
||||
# (git-ignored):
|
||||
# MARKETING_VERSION -> CFBundleShortVersionString (the git tag, e.g. 4.0.0)
|
||||
# CURRENT_PROJECT_VERSION -> CFBundleVersion (integer build number)
|
||||
#
|
||||
# Both are guaranteed non-empty (falling back to 1.0 / 1 when no tag is
|
||||
# reachable) because an app extension whose CFBundleVersion resolves to an empty
|
||||
# string fails to install.
|
||||
# `Version.xcconfig` (committed, read by every target) optionally #includes this
|
||||
# file, so these values override the committed 1.0 / 1 fallbacks when present.
|
||||
# Unlike the previous `${...}` XcodeGen env-substitution approach, forgetting to
|
||||
# run this script no longer produces an EMPTY CFBundleVersion (which makes the
|
||||
# app-extension install fail with "bundleVersion must be set in placeholder
|
||||
# attributes for an app extension placeholder") — the committed fallbacks keep
|
||||
# the build installable.
|
||||
#
|
||||
# The values are guaranteed non-empty (falling back to 1.0 / 1 when no tag is
|
||||
# reachable). This script can be `source`d or executed; it exports the same two
|
||||
# variables too, for backward compatibility with tooling that reads them.
|
||||
|
||||
# Resolve this script's directory so it works regardless of the current
|
||||
# working directory (and whether sourced or executed).
|
||||
@@ -43,6 +51,17 @@ _version_code=$(( _version_major * 100000000 + _version_minor * 100000 + _versio
|
||||
export MARKETING_VERSION="$_version_name"
|
||||
export CURRENT_PROJECT_VERSION="$_version_code"
|
||||
|
||||
echo "iOS version from git: MARKETING_VERSION=$MARKETING_VERSION CURRENT_PROJECT_VERSION=$CURRENT_PROJECT_VERSION"
|
||||
# Write the git-ignored local override that Version.xcconfig optionally includes.
|
||||
_version_xcconfig="$_version_script_dir/Version.local.xcconfig"
|
||||
cat > "$_version_xcconfig" <<EOF
|
||||
// Generated by iosApp/version.sh — do not commit (git-ignored).
|
||||
// Overrides the committed defaults in Version.xcconfig with the git-derived
|
||||
// version. Re-run version.sh to refresh.
|
||||
MARKETING_VERSION = $MARKETING_VERSION
|
||||
CURRENT_PROJECT_VERSION = $CURRENT_PROJECT_VERSION
|
||||
EOF
|
||||
|
||||
unset _version_script_dir _version_name _version_major _version_minor _version_patch _version_code
|
||||
echo "iOS version from git: MARKETING_VERSION=$MARKETING_VERSION CURRENT_PROJECT_VERSION=$CURRENT_PROJECT_VERSION"
|
||||
echo "Wrote $_version_xcconfig"
|
||||
|
||||
unset _version_script_dir _version_name _version_major _version_minor _version_patch _version_code _version_xcconfig
|
||||
|
||||
Reference in New Issue
Block a user