From 16c2ada09b302ca51e00d9179762071512b1d2e4 Mon Sep 17 00:00:00 2001 From: Brent Perteet Date: Fri, 21 Aug 2026 12:33:11 -0500 Subject: [PATCH] Configure UM Trace iOS signing and deployment --- .gitignore | 12 +++ FieldLogger/FieldLogger.csproj | 8 +- FieldLogger/Platforms/iOS/Info.plist | 15 +++- .../iOS/Resources/PrivacyInfo.xcprivacy | 4 +- doc/iOS_TestFlight.md | 77 +++++++++++++++++++ scripts/deploy-ios-device.sh | 50 ++++++++++++ scripts/publish-testflight.sh | 49 ++++++++++++ 7 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 doc/iOS_TestFlight.md create mode 100755 scripts/deploy-ios-device.sh create mode 100755 scripts/publish-testflight.sh diff --git a/.gitignore b/.gitignore index ffcac66..d8ccdec 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,18 @@ # dotenv files .env +# Local iOS deployment target (contains a personal device UDID). +.ios-device + +# Apple signing credentials and provisioning artifacts. Keep these in Keychain or +# an approved secret store; never commit them to the application repository. +*.cer +*.p12 +*.p8 +*.mobileprovision +*.provisionprofile +*.certSigningRequest + # User-specific files *.rsuser *.suo diff --git a/FieldLogger/FieldLogger.csproj b/FieldLogger/FieldLogger.csproj index 35c2479..b11545b 100644 --- a/FieldLogger/FieldLogger.csproj +++ b/FieldLogger/FieldLogger.csproj @@ -26,10 +26,10 @@ $(NoWarn);MVVMTK0045 - Field Logger + UM Trace - com.undergroundmagnetics.fieldlogger + com.umagul.trace 1.0 @@ -46,6 +46,10 @@ 6.5 + + + ITSAppUsesNonExemptEncryption + diff --git a/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy b/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy index 24ab3b4..a93de40 100644 --- a/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy +++ b/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy @@ -36,8 +36,6 @@ More information: https://aka.ms/maui-privacy-manifest E174.1 - + diff --git a/doc/iOS_TestFlight.md b/doc/iOS_TestFlight.md new file mode 100644 index 0000000..1f6889e --- /dev/null +++ b/doc/iOS_TestFlight.md @@ -0,0 +1,77 @@ +# UM Trace TestFlight build + +## Fixed application identity + +- Display name: `UM Trace` +- Bundle ID: `com.umagul.trace` +- Apple team ID: `W2N8APPQ2C` +- App Store Connect SKU: `UM-TRACE-IOS` +- Distribution profile: `UM Trace App Store` + +The provisioning profile and all Apple signing files are local-only and covered by +`.gitignore`. Never commit a certificate, private key, App Store Connect API key, `.p12`, or +provisioning profile. + +## Required local toolchain + +- Xcode 26 at `/Applications/Xcode.app` +- .NET SDK 10.0.100 as the build host +- Workload set 10.0.100 with `maui-ios` and its .NET 9/iOS 26 compatibility pack +- An `Apple Distribution` identity, including its private key, in the login Keychain +- The `UM Trace App Store` provisioning profile installed locally + +This repository's local SDK is installed at `~/.dotnet`. Verify it with: + +```sh +~/.dotnet/dotnet --info +~/.dotnet/dotnet workload list +``` + +## Create an archive + +Run the release helper from a normal macOS Terminal session. The build number must increase for +every upload to App Store Connect: + +```sh +cd /Users/brent/ul-platform/app +./scripts/publish-testflight.sh 1 +``` + +The app remains on its existing .NET 9 target. The .NET 10 SDK host supplies the supported .NET 9 +compatibility pack for Xcode 26, avoiding an unrelated Android/Mac Catalyst framework migration. +The script deliberately supplies Xcode through `DEVELOPER_DIR`; it does not change the machine's +global `xcode-select` setting. It restores only the iOS target, builds with the iOS 26 SDK, selects +the installed `UM Trace App Store` profile, and prints the generated `.ipa` path. + +Before uploading, verify that this command lists a valid distribution identity: + +```sh +security find-identity -v -p codesigning +``` + +If the identity is absent, import a `.p12` containing the matching private key or create a new +Apple Distribution certificate through Xcode and regenerate the provisioning profile against it. + +## Deploy a development build to an iPhone + +The local development profile is named `UM Trace Development`. It must include the target phone +and match an `Apple Development` identity for team `W2N8APPQ2C`. Keep the phone unlocked and +connected by USB for the first deployment, trust the Mac when prompted, and enable Developer Mode. + +Save the phone UDID in the gitignored `.ios-device` file, then run from a normal macOS Terminal: + +```sh +cd /Users/brent/ul-platform/app +./scripts/deploy-ios-device.sh +``` + +You can instead pass a UDID as the first argument or set `UM_TRACE_DEVICE_UDID`. The helper uses +Xcode 26, builds the `Debug` configuration for `ios-arm64`, signs with the development profile, +installs the app, and launches it on the selected phone. + +## Background and privacy declarations + +The iOS bundle declares `bluetooth-central` and `location` background modes, Bluetooth and +always/when-in-use location explanations, the MAUI `UserDefaults` required-reason API, and exempt +standard TLS use. Physical-device testing remains required to prove the SRS eight-hour background +BLE/location session; declaring a background mode does not itself guarantee continuous execution. diff --git a/scripts/deploy-ios-device.sh b/scripts/deploy-ios-device.sh new file mode 100755 index 0000000..7da7190 --- /dev/null +++ b/scripts/deploy-ios-device.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +dotnet_bin="${DOTNET_BIN:-${HOME}/.dotnet/dotnet}" +developer_dir="${DEVELOPER_DIR:-/Applications/Xcode.app/Contents/Developer}" +codesign_key="${UM_TRACE_DEVELOPMENT_KEY:-Apple Development: Brent Perteet (WZ56HVC6FD)}" +codesign_profile="${UM_TRACE_DEVELOPMENT_PROVISION:-UM Trace Development}" +device_udid="${1:-${UM_TRACE_DEVICE_UDID:-}}" + +if [[ -z "${device_udid}" && -f "${repo_dir}/.ios-device" ]]; then + IFS= read -r device_udid < "${repo_dir}/.ios-device" +fi + +if [[ -z "${device_udid}" ]]; then + echo "Usage: $0 " >&2 + echo "Alternatively, set UM_TRACE_DEVICE_UDID or save the UDID in ${repo_dir}/.ios-device." >&2 + exit 2 +fi + +if [[ ! -x "${dotnet_bin}" ]]; then + echo "Missing .NET SDK at ${dotnet_bin}. Set DOTNET_BIN or install the local SDK." >&2 + exit 2 +fi + +if [[ ! -d "${developer_dir}" ]]; then + echo "Missing Xcode developer directory at ${developer_dir}. Set DEVELOPER_DIR." >&2 + exit 2 +fi + +export DEVELOPER_DIR="${developer_dir}" + +"${dotnet_bin}" restore "${repo_dir}/FieldLogger/FieldLogger.csproj" \ + -p:TargetFrameworks=net9.0-ios \ + -p:RuntimeIdentifier=ios-arm64 + +# Restoring the MAUI head with a narrowed TargetFrameworks property can rewrite the +# referenced headless library's assets file. Restore that library explicitly before building. +"${dotnet_bin}" restore "${repo_dir}/src/FieldLogger.Sync/FieldLogger.Sync.csproj" + +"${dotnet_bin}" build "${repo_dir}/FieldLogger/FieldLogger.csproj" \ + -t:Run \ + -p:TargetFrameworks=net9.0-ios \ + -f net9.0-ios \ + -c Debug \ + -p:RuntimeIdentifier=ios-arm64 \ + -p:_DeviceName="${device_udid}" \ + -p:CodesignKey="${codesign_key}" \ + -p:CodesignProvision="${codesign_profile}" \ + --no-restore diff --git a/scripts/publish-testflight.sh b/scripts/publish-testflight.sh new file mode 100755 index 0000000..8eda0cf --- /dev/null +++ b/scripts/publish-testflight.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +dotnet_bin="${DOTNET_BIN:-${HOME}/.dotnet/dotnet}" +developer_dir="${DEVELOPER_DIR:-/Applications/Xcode.app/Contents/Developer}" +build_number="${1:-}" +codesign_key="${UM_TRACE_CODESIGN_KEY:-Apple Distribution}" +codesign_profile="${UM_TRACE_CODESIGN_PROVISION:-UM Trace App Store}" + +if [[ ! "${build_number}" =~ ^[0-9]+$ ]]; then + echo "Usage: $0 " >&2 + exit 2 +fi + +if [[ ! -x "${dotnet_bin}" ]]; then + echo "Missing .NET 10 SDK at ${dotnet_bin}. Set DOTNET_BIN or install .NET 10." >&2 + exit 2 +fi + +if [[ ! -d "${developer_dir}" ]]; then + echo "Missing Xcode developer directory at ${developer_dir}. Set DEVELOPER_DIR." >&2 + exit 2 +fi + +export DEVELOPER_DIR="${developer_dir}" + +"${dotnet_bin}" restore "${repo_dir}/FieldLogger/FieldLogger.csproj" \ + -p:TargetFrameworks=net9.0-ios \ + -p:RuntimeIdentifier=ios-arm64 + +# Restoring the MAUI head with a narrowed TargetFrameworks property can rewrite the +# referenced headless library's assets file. Restore that library explicitly before publish. +"${dotnet_bin}" restore "${repo_dir}/src/FieldLogger.Sync/FieldLogger.Sync.csproj" + +"${dotnet_bin}" publish "${repo_dir}/FieldLogger/FieldLogger.csproj" \ + -p:TargetFrameworks=net9.0-ios \ + -f net9.0-ios \ + -c Release \ + -p:RuntimeIdentifier=ios-arm64 \ + -p:ArchiveOnBuild=true \ + -p:BuildIpa=true \ + -p:CodesignKey="${codesign_key}" \ + -p:CodesignProvision="${codesign_profile}" \ + -p:ApplicationVersion="${build_number}" \ + --no-restore + +find "${repo_dir}/FieldLogger/bin/Release/net9.0-ios/ios-arm64" \ + -type f -name '*.ipa' -print