Configure UM Trace iOS signing and deployment
This commit is contained in:
12
.gitignore
vendored
12
.gitignore
vendored
@@ -6,6 +6,18 @@
|
|||||||
# dotenv files
|
# dotenv files
|
||||||
.env
|
.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
|
# User-specific files
|
||||||
*.rsuser
|
*.rsuser
|
||||||
*.suo
|
*.suo
|
||||||
|
|||||||
@@ -26,10 +26,10 @@
|
|||||||
<NoWarn>$(NoWarn);MVVMTK0045</NoWarn>
|
<NoWarn>$(NoWarn);MVVMTK0045</NoWarn>
|
||||||
|
|
||||||
<!-- Display name -->
|
<!-- Display name -->
|
||||||
<ApplicationTitle>Field Logger</ApplicationTitle>
|
<ApplicationTitle>UM Trace</ApplicationTitle>
|
||||||
|
|
||||||
<!-- App Identifier -->
|
<!-- App Identifier -->
|
||||||
<ApplicationId>com.undergroundmagnetics.fieldlogger</ApplicationId>
|
<ApplicationId>com.umagul.trace</ApplicationId>
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
|
||||||
@@ -46,6 +46,10 @@
|
|||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Apple Developer team used for iOS distribution: W2N8APPQ2C.
|
||||||
|
Certificate/profile selection stays outside source control and is supplied by
|
||||||
|
Keychain plus the installed com.umagul.trace provisioning profile at publish time. -->
|
||||||
|
|
||||||
<!-- SEC-1: the Google Maps Android key is injected at build time, never committed.
|
<!-- SEC-1: the Google Maps Android key is injected at build time, never committed.
|
||||||
Resolution order: (1) MapsApiKey MSBuild property (pass -p:MapsApiKey=... in CI from a
|
Resolution order: (1) MapsApiKey MSBuild property (pass -p:MapsApiKey=... in CI from a
|
||||||
secret), else (2) the MAPS_API_KEY environment variable, else (3) a gitignored
|
secret), else (2) the MAPS_API_KEY environment variable, else (3) a gitignored
|
||||||
|
|||||||
@@ -29,8 +29,19 @@
|
|||||||
<key>XSAppIconAssets</key>
|
<key>XSAppIconAssets</key>
|
||||||
<string>Assets.xcassets/appicon.appiconset</string>
|
<string>Assets.xcassets/appicon.appiconset</string>
|
||||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||||
<string>Field Logger uses Bluetooth to connect to your Underground Magnetics locating receiver and Maglink RTK GPS receiver.</string>
|
<string>UM Trace uses Bluetooth to connect to your Underground Magnetics locating receiver and MagLink RTK GNSS receiver.</string>
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<string>Field Logger uses your location to display logged points on the map.</string>
|
<string>UM Trace uses your location to display and record georeferenced locate points during an active field session.</string>
|
||||||
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
|
<string>UM Trace uses your location during an active locating session to keep georeferenced field records current while the app is in the background.</string>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>bluetooth-central</string>
|
||||||
|
<string>location</string>
|
||||||
|
</array>
|
||||||
|
<!-- The app uses standard, exempt TLS for HTTPS and MQTTS/WSS transport; it does
|
||||||
|
not implement proprietary or non-standard cryptography. -->
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ More information: https://aka.ms/maui-privacy-manifest
|
|||||||
<string>E174.1</string>
|
<string>E174.1</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
<!--
|
|
||||||
The entry below is only needed when you're using the Preferences API in your app.
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
@@ -45,7 +43,7 @@ More information: https://aka.ms/maui-privacy-manifest
|
|||||||
<array>
|
<array>
|
||||||
<string>CA92.1</string>
|
<string>CA92.1</string>
|
||||||
</array>
|
</array>
|
||||||
</dict> -->
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
77
doc/iOS_TestFlight.md
Normal file
77
doc/iOS_TestFlight.md
Normal file
@@ -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.
|
||||||
50
scripts/deploy-ios-device.sh
Executable file
50
scripts/deploy-ios-device.sh
Executable file
@@ -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 <device-udid>" >&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
|
||||||
49
scripts/publish-testflight.sh
Executable file
49
scripts/publish-testflight.sh
Executable file
@@ -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 <numeric-build-number>" >&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
|
||||||
Reference in New Issue
Block a user