Workouts 2.0: re-base persistence on iCloud Drive documents

Replace Core Data + NSPersistentCloudKitContainer + App-Group store +
WatchConnectivity dictionary sync with the QuickRabbit iCloud-documents
architecture:

- iCloud Drive JSON documents are the sole source of truth (one file per
  aggregate: Splits/<ULID>.json, Workouts/YYYY/MM/<ULID>.json), with a
  rebuildable SwiftData cache populated only by an NSMetadataQuery observer
  and a connect-time reconcile. Soft-delete tombstones; hard iCloud gate.
- Shared model layer (ULID, Codable *Documents + stateless mappers, @Model
  cache entities, SwiftData container) compiled into both targets.
- New iPhone<->Watch bridge over WatchConnectivity keyed by ULIDs; the phone
  is the sole writer of iCloud Drive, the watch round-trips documents.
- AppServices DI + iCloud-required root gate; Swift 6 strict concurrency.
- Starter splits generated on demand from the bundled YAML catalogs.
- Migrate to XcodeGen (project.yml), iOS 26 / watchOS 26; CloudDocuments
  entitlement (drop CloudKit/App Group/aps-environment).
- Duration stored as Int seconds (was a Date epoch hack); fix workout
  end-on-create, undismissable delete dialog, toolbar-hiding nav stacks,
  and the Settings placeholder.
- Add README/CHANGELOG/LICENSE, .gitignore, refreshed REQUIREMENTS, and the
  Scripts/ TestFlight pipeline (release.sh + ASC API scripts).

MARKETING_VERSION 2.0.
This commit is contained in:
2026-06-19 14:25:27 -04:00
parent 9a881e841b
commit 85d0eaddbb
86 changed files with 3873 additions and 3755 deletions
+79
View File
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
#
# release.sh — archive Workouts (iOS app with the embedded watch app) and upload
# to TestFlight / App Store Connect. No third-party tooling: pure xcodebuild + the
# App Store Connect API key. Credentials live in .env.release (gitignored); see
# .env.release.example.
#
# What it does:
# 1. Regenerates the Xcode project with XcodeGen.
# 2. Stamps CFBundleVersion (CURRENT_PROJECT_VERSION) with the git commit count
# for both the iOS app and the embedded watch app.
# 3. xcodebuild archive (the watch app rides along in the same archive)
# 4. xcodebuild -exportArchive with destination=upload -> App Store Connect.
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$ROOT"
PROJECT="Workouts.xcodeproj"
SCHEME="Workouts"
BUILD_DIR="$ROOT/build"
# ---- credentials ------------------------------------------------------------
ENV_FILE="$ROOT/.env.release"
[ -f "$ENV_FILE" ] && { set -a; . "$ENV_FILE"; set +a; }
: "${APPLE_TEAM_ID:?Set APPLE_TEAM_ID (copy .env.release.example -> .env.release)}"
: "${ASC_KEY_ID:?Set ASC_KEY_ID in .env.release}"
: "${ASC_ISSUER_ID:?Set ASC_ISSUER_ID in .env.release}"
: "${ASC_KEY_PATH:?Set ASC_KEY_PATH in .env.release}"
[ -f "$ASC_KEY_PATH" ] || { echo "❌ API key not found at: $ASC_KEY_PATH"; exit 1; }
AUTH=(
-authenticationKeyPath "$ASC_KEY_PATH"
-authenticationKeyID "$ASC_KEY_ID"
-authenticationKeyIssuerID "$ASC_ISSUER_ID"
-allowProvisioningUpdates
)
# ---- regenerate project -----------------------------------------------------
if command -v xcodegen >/dev/null 2>&1; then
echo "🧩 Generating $PROJECT ..."
xcodegen generate
elif [ ! -d "$PROJECT" ]; then
echo "$PROJECT missing and xcodegen not installed."; exit 1
fi
# ---- versioning -------------------------------------------------------------
BUILD_NUMBER="$(git rev-list HEAD --count)"
MARKETING_VERSION="$(grep -m1 'MARKETING_VERSION:' project.yml | sed -E 's/.*"([^"]+)".*/\1/')"
echo "📦 Version $MARKETING_VERSION (build $BUILD_NUMBER)"
mkdir -p "$BUILD_DIR"
ARCHIVE="$BUILD_DIR/Workouts.xcarchive"
EXPORT="$BUILD_DIR/Workouts-export"
rm -rf "$ARCHIVE" "$EXPORT"
echo "🛠 Archiving (iOS app + embedded watch app) ..."
xcodebuild archive \
-project "$PROJECT" \
-scheme "$SCHEME" \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath "$ARCHIVE" \
CURRENT_PROJECT_VERSION="$BUILD_NUMBER" \
CODE_SIGN_STYLE=Automatic \
DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
"${AUTH[@]}"
echo "🚀 Exporting + uploading to App Store Connect ..."
xcodebuild -exportArchive \
-archivePath "$ARCHIVE" \
-exportPath "$EXPORT" \
-exportOptionsPlist "$SCRIPT_DIR/ExportOptions-iOS.plist" \
"${AUTH[@]}"
echo ""
echo "✅ Uploaded build $BUILD_NUMBER. Appears in App Store Connect > TestFlight after processing (~515 min)."