From 60a7de7540ed1a13f98ec1639f332f5c3ffdb9a5 Mon Sep 17 00:00:00 2001 From: Ralf Wondratschek Date: Sat, 11 Apr 2026 16:36:36 -0700 Subject: [PATCH] Add interactive iOS app runner script Add a repo-local ios-run.sh helper for the sample and recipes iOS wrapper apps. The script: - prompts for which app to run - reuses the only booted simulator when possible - prompts for a simulator when none or multiple are booted - boots the selected simulator if needed - builds the selected Xcode wrapper app - installs the built .app into the chosen simulator - launches the installed app by bundle identifier --- ios-run.sh | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100755 ios-run.sh diff --git a/ios-run.sh b/ios-run.sh new file mode 100755 index 00000000..4428e01f --- /dev/null +++ b/ios-run.sh @@ -0,0 +1,228 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" + +APP_KEY="" +APP_LABEL="" +PROJECT_PATH="" +SCHEME="" +DERIVED_DATA_PATH="" + +SIMULATOR_NAMES=() +SIMULATOR_UDIDS=() +SIMULATOR_STATES=() +SIMULATOR_RUNTIMES=() +PROMPT_SELECTION_INDEX="" +SELECTED_SIMULATOR_UDID="" + +require_command() { + local command_name="$1" + if ! command -v "$command_name" >/dev/null 2>&1; then + echo "Missing required command: $command_name" >&2 + exit 1 + fi +} + +choose_application() { + while true; do + cat <<'EOF' +Which application do you want to run? +1) sample +2) recipes +EOF + printf "Enter selection [1-2]: " + read -r selection + + case "$selection" in + 1) + APP_KEY="sample" + APP_LABEL="sample" + PROJECT_PATH="$ROOT_DIR/sample/iosApp/iosApp.xcodeproj" + SCHEME="iosApp" + DERIVED_DATA_PATH="/tmp/app-platform-ios-run-sample" + return + ;; + 2) + APP_KEY="recipes" + APP_LABEL="recipes" + PROJECT_PATH="$ROOT_DIR/recipes/recipesIosApp/recipesIosApp.xcodeproj" + SCHEME="recipesIosApp" + DERIVED_DATA_PATH="/tmp/app-platform-ios-run-recipes" + return + ;; + *) + echo "Invalid selection." + ;; + esac + done +} + +load_simulators() { + local mode="$1" + local current_runtime="" + local line="" + local parsed_line="" + local device_name="" + local device_udid="" + local device_state="" + + SIMULATOR_NAMES=() + SIMULATOR_UDIDS=() + SIMULATOR_STATES=() + SIMULATOR_RUNTIMES=() + + if [[ "$mode" == "available" ]]; then + while IFS= read -r line; do + parsed_line="$(printf '%s\n' "$line" | sed -nE 's/^-- (.+) --$/\1/p')" + if [[ -n "$parsed_line" ]]; then + current_runtime="$parsed_line" + continue + fi + + if [[ "$current_runtime" != iOS* ]]; then + continue + fi + + parsed_line="$(printf '%s\n' "$line" | sed -nE 's/^[[:space:]]+(.+) \(([A-F0-9-]+)\) \(([^)]+)\)[[:space:]]*$/\1\t\2\t\3/p')" + if [[ -n "$parsed_line" ]]; then + IFS=$'\t' read -r device_name device_udid device_state <<<"$parsed_line" + SIMULATOR_NAMES+=("$device_name") + SIMULATOR_UDIDS+=("$device_udid") + SIMULATOR_STATES+=("$device_state") + SIMULATOR_RUNTIMES+=("$current_runtime") + fi + done < <(xcrun simctl list devices available) + else + while IFS= read -r line; do + parsed_line="$(printf '%s\n' "$line" | sed -nE 's/^-- (.+) --$/\1/p')" + if [[ -n "$parsed_line" ]]; then + current_runtime="$parsed_line" + continue + fi + + if [[ "$current_runtime" != iOS* ]]; then + continue + fi + + parsed_line="$(printf '%s\n' "$line" | sed -nE 's/^[[:space:]]+(.+) \(([A-F0-9-]+)\) \(([^)]+)\)[[:space:]]*$/\1\t\2\t\3/p')" + if [[ -n "$parsed_line" ]]; then + IFS=$'\t' read -r device_name device_udid device_state <<<"$parsed_line" + if [[ "$device_state" != "Booted" ]]; then + continue + fi + SIMULATOR_NAMES+=("$device_name") + SIMULATOR_UDIDS+=("$device_udid") + SIMULATOR_STATES+=("$device_state") + SIMULATOR_RUNTIMES+=("$current_runtime") + fi + done < <(xcrun simctl list devices) + fi +} + +prompt_for_simulator_index() { + local prompt="$1" + local max_index="${#SIMULATOR_NAMES[@]}" + local i="" + local selection="" + + if (( max_index == 0 )); then + echo "No matching simulators found." >&2 + exit 1 + fi + + echo "$prompt" >&2 + for (( i = 0; i < max_index; i++ )); do + printf "%d) %s [%s] (%s)\n" \ + "$((i + 1))" \ + "${SIMULATOR_NAMES[$i]}" \ + "${SIMULATOR_RUNTIMES[$i]}" \ + "${SIMULATOR_STATES[$i]}" >&2 + done + + while true; do + printf "Enter selection [1-%d]: " "$max_index" >&2 + read -r selection + if [[ "$selection" =~ ^[0-9]+$ ]] && (( selection >= 1 && selection <= max_index )); then + PROMPT_SELECTION_INDEX="$((selection - 1))" + return + fi + echo "Invalid selection." >&2 + done +} + +choose_simulator() { + local selected_udid="" + + load_simulators "booted" + + case "${#SIMULATOR_UDIDS[@]}" in + 0) + load_simulators "available" + prompt_for_simulator_index "No simulator is booted. Which simulator should be booted?" + selected_udid="${SIMULATOR_UDIDS[$PROMPT_SELECTION_INDEX]}" + echo "Booting ${SIMULATOR_NAMES[$PROMPT_SELECTION_INDEX]}..." >&2 + xcrun simctl boot "$selected_udid" + open -a Simulator --args -CurrentDeviceUDID "$selected_udid" >/dev/null 2>&1 || open -a Simulator >/dev/null 2>&1 || true + xcrun simctl bootstatus "$selected_udid" -b + SELECTED_SIMULATOR_UDID="$selected_udid" + ;; + 1) + echo "Using booted simulator: ${SIMULATOR_NAMES[0]} [${SIMULATOR_RUNTIMES[0]}]" >&2 + SELECTED_SIMULATOR_UDID="${SIMULATOR_UDIDS[0]}" + ;; + *) + prompt_for_simulator_index "More than one simulator is booted. Which one should be used?" + SELECTED_SIMULATOR_UDID="${SIMULATOR_UDIDS[$PROMPT_SELECTION_INDEX]}" + ;; + esac +} + +find_built_app() { + local products_dir="$1/Build/Products/Debug-iphonesimulator" + find "$products_dir" -maxdepth 1 -type d -name '*.app' | head -n 1 +} + +main() { + local app_path="" + local bundle_id="" + + if [[ "$(uname -s)" != "Darwin" ]]; then + echo "This script requires macOS." >&2 + exit 1 + fi + + require_command xcodebuild + require_command xcrun + require_command open + require_command /usr/libexec/PlistBuddy + + choose_application + choose_simulator + + echo "Building $APP_LABEL for simulator $SELECTED_SIMULATOR_UDID..." + xcodebuild \ + -project "$PROJECT_PATH" \ + -scheme "$SCHEME" \ + -configuration Debug \ + -destination "platform=iOS Simulator,id=$SELECTED_SIMULATOR_UDID" \ + -derivedDataPath "$DERIVED_DATA_PATH" \ + build + + app_path="$(find_built_app "$DERIVED_DATA_PATH")" + if [[ -z "$app_path" ]]; then + echo "Could not find the built .app bundle in $DERIVED_DATA_PATH." >&2 + exit 1 + fi + + bundle_id="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$app_path/Info.plist")" + + echo "Installing $app_path..." + xcrun simctl install "$SELECTED_SIMULATOR_UDID" "$app_path" + + echo "Launching $bundle_id..." + xcrun simctl launch "$SELECTED_SIMULATOR_UDID" "$bundle_id" +} + +main "$@"