#!/bin/bash # install.sh — build FuelBoard dev-signed and install straight to a paired iPhone. # # Streamlined flow (single command): # ./scripts/install.sh # build + install + launch on the first paired iPhone # ./scripts/install.sh --device UDID # target a specific device (xcrun devicectl list devices) # ./scripts/install.sh --no-launch # install but don't auto-launch # # Why this exists: sideloading (SideStore/AltStore) got replaced on 2026-09-25 by a # proper dev-signed install via devicectl — the phone is paired, so no LAN server, # no delete/reinstall dance, and the installed app is a real signed build. The # Release config targets the device (not the simulator) and reuses Xcode automatic # signing with the project's DEVELOPMENT_TEAM. # # Requires: a paired + unlocked iPhone (xcrun devicectl list devices), and the # Apple Development identity in the keychain. set -euo pipefail cd "$(dirname "$0")/.." PROJECT="$PWD/FuelBoard.xcodeproj" SCHEME="FuelBoard" DERIVED_DATA="/tmp/fuelboard-devinstall" APP="$DERIVED_DATA/Build/Products/Release-iphoneos/FuelBoard.app" DEVICE="" DEVICE_NAME="(first paired iPhone)" LAUNCH=1 while [[ $# -gt 0 ]]; do case "$1" in --device) DEVICE="$2"; shift 2 ;; --no-launch) LAUNCH=0; shift ;; *) echo "unknown arg: $1" >&2; exit 1 ;; esac done # Resolve the device if not given: the first connected physical iPhone. # devicectl --device wants the CoreDevice Identifier (field 3 of `list devices`, # e.g. C519BE18-...). NOTE: xcodebuild -destination wants the hardware UDID # (00008130-...), so we build with the generic platform destination and only use # the CoreDevice Identifier for devicectl install/launch. if [[ -z "$DEVICE" ]]; then DEVICE="$(xcrun devicectl list devices 2>/dev/null | awk '/(available \(paired\))/ && /iPhone/ {print $3; exit}')" if [[ -z "$DEVICE" ]]; then echo "install.sh: no paired iPhone found. Check: xcrun devicectl list devices" >&2 exit 1 fi fi echo "==> Build (Release, dev-signed, generic iOS destination)" rm -rf "$DERIVED_DATA" xcodebuild -project "$PROJECT" -scheme "$SCHEME" -configuration Release \ -destination 'generic/platform=iOS' \ -derivedDataPath "$DERIVED_DATA" \ CODE_SIGN_STYLE=Automatic \ build 2>&1 | grep -E "error:|BUILD (SUCCEEDED|FAILED)|Signing Identity" | tail -8 echo "==> Verify bundle" test -d "$APP" || { echo "build failed: no app at $APP" >&2; exit 1; } echo " bundle: $(plutil -extract CFBundleIdentifier raw "$APP/Info.plist")" \ " $(plutil -extract CFBundleShortVersionString raw "$APP/Info.plist")" \ "($(plutil -extract CFBundleVersion raw "$APP/Info.plist"))" echo "==> Install via devicectl" xcrun devicectl device install app --device "$DEVICE" "$APP" 2>&1 | tail -6 if [[ "$LAUNCH" -eq 1 ]]; then BID="$(plutil -extract CFBundleIdentifier raw "$APP/Info.plist")" echo "==> Launch" xcrun devicectl device process launch --device "$DEVICE" "$BID" 2>&1 | tail -4 fi echo "==> Done ✅ (delete the old icon on the phone if you want it refreshed)"