Created
November 18, 2025 10:57
-
-
Save xissy/495ec0042015d7e530cfdb357a108f5f to your computer and use it in GitHub Desktop.
Universal iOS Build and Launch Script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Universal iOS Build and Launch Script | |
| # Automatically detects project settings or accepts command-line options | |
| # | |
| # Usage: ./ios-build-and-launch.sh [OPTIONS] [device-name] | |
| # | |
| # Options: | |
| # --project PATH Path to .xcodeproj or .xcworkspace (auto-detected if not provided) | |
| # --scheme NAME Scheme name (auto-detected if not provided) | |
| # --bundle-id ID Bundle identifier (auto-detected if not provided) | |
| # --configuration CFG Build configuration (default: Debug) | |
| # --simulator, -s Search only in simulators | |
| # --device, -d Search only in physical devices | |
| # --help, -h Show this help message | |
| # | |
| # Examples: | |
| # ./ios-build-and-launch.sh "iPhone 17 Pro" | |
| # ./ios-build-and-launch.sh --simulator "iPhone 17 Pro" | |
| # ./ios-build-and-launch.sh --device "My iPhone" | |
| # ./ios-build-and-launch.sh --scheme MyScheme --configuration Release "iPhone 17 Pro" | |
| # | |
| # GitHub: https://gist.github.com/[your-gist-id] | |
| # License: MIT | |
| set -e # Exit on error | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' # No Color | |
| # Default configuration | |
| PROJECT_PATH="" | |
| SCHEME="" | |
| BUNDLE_ID="" | |
| CONFIGURATION="Debug" | |
| FORCE_TYPE="" # "simulator", "device", or "" (auto-detect) | |
| DEVICE_NAME="" | |
| # Get script directory and working directory | |
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | |
| WORKING_DIR="$(pwd)" | |
| # Function to show help | |
| show_help() { | |
| echo -e "${BLUE}Universal iOS Build and Launch Script${NC}" | |
| echo -e "${BLUE}======================================${NC}\n" | |
| echo "Automatically detects project settings or accepts command-line options" | |
| echo "" | |
| echo "Usage: $0 [OPTIONS] [device-name]" | |
| echo "" | |
| echo "Options:" | |
| echo " --project PATH Path to .xcodeproj or .xcworkspace (auto-detected if not provided)" | |
| echo " --scheme NAME Scheme name (auto-detected if not provided)" | |
| echo " --bundle-id ID Bundle identifier (auto-detected if not provided)" | |
| echo " --configuration CFG Build configuration (default: Debug)" | |
| echo " --simulator, -s Search only in simulators" | |
| echo " --device, -d Search only in physical devices" | |
| echo " --help, -h Show this help message" | |
| echo "" | |
| echo "Examples:" | |
| echo " $0 \"iPhone 17 Pro\"" | |
| echo " $0 --simulator \"iPhone 17 Pro\"" | |
| echo " $0 --device \"My iPhone\"" | |
| echo " $0 --scheme MyScheme --configuration Release \"iPhone 17 Pro\"" | |
| echo "" | |
| } | |
| # Function to auto-detect project file | |
| detect_project() { | |
| # First try to find .xcworkspace (preferred) | |
| local workspace=$(find "$WORKING_DIR" -maxdepth 1 -name "*.xcworkspace" -type d | head -1) | |
| if [ -n "$workspace" ]; then | |
| echo "$workspace" | |
| return 0 | |
| fi | |
| # Fall back to .xcodeproj | |
| local project=$(find "$WORKING_DIR" -maxdepth 1 -name "*.xcodeproj" -type d | head -1) | |
| if [ -n "$project" ]; then | |
| echo "$project" | |
| return 0 | |
| fi | |
| return 1 | |
| } | |
| # Function to auto-detect scheme using hybrid 3-tier approach | |
| detect_scheme() { | |
| local project_path="$1" | |
| local project_flag="" | |
| local xcodeproj_path="" | |
| # Get project name for matching | |
| local project_name=$(basename "$project_path" | sed 's/\.[^.]*$//') | |
| if [[ "$project_path" == *.xcworkspace ]]; then | |
| project_flag="-workspace" | |
| # For workspace, find the .xcodeproj inside | |
| xcodeproj_path=$(find "$WORKING_DIR" -maxdepth 1 -name "*.xcodeproj" -type d | head -1) | |
| else | |
| project_flag="-project" | |
| xcodeproj_path="$project_path" | |
| fi | |
| # =================================================================== | |
| # TIER 1: Check shared scheme files (most reliable) | |
| # =================================================================== | |
| # Shared schemes exclude auto-generated SPM package schemes | |
| if [ -n "$xcodeproj_path" ]; then | |
| local shared_schemes_dir="$xcodeproj_path/xcshareddata/xcschemes" | |
| if [ -d "$shared_schemes_dir" ]; then | |
| # Get all .xcscheme files | |
| local scheme_files=$(find "$shared_schemes_dir" -name "*.xcscheme" -type f 2>/dev/null) | |
| if [ -n "$scheme_files" ]; then | |
| # Parse each scheme file to find main app schemes | |
| local main_schemes=() | |
| while IFS= read -r scheme_file; do | |
| # Check if scheme is for a main app (.app) and has LaunchAction | |
| local buildable_name=$(grep -A 2 "BuildableName" "$scheme_file" 2>/dev/null | grep "BuildableName = " | head -1) | |
| local has_launch_action=$(grep -c "<LaunchAction" "$scheme_file" 2>/dev/null || echo "0") | |
| # Filter: BuildableName ends with .app and has LaunchAction | |
| if [[ "$buildable_name" =~ \.app\" ]] && [ "$has_launch_action" -gt 0 ]; then | |
| # Exclude extension/widget/watch schemes | |
| local scheme_basename=$(basename "$scheme_file" .xcscheme) | |
| if ! [[ "$scheme_basename" =~ (Extension|Widget|Watch|Tests)$ ]]; then | |
| main_schemes+=("$scheme_basename") | |
| fi | |
| fi | |
| done <<< "$scheme_files" | |
| # If we found main app schemes | |
| if [ ${#main_schemes[@]} -gt 0 ]; then | |
| # Prefer scheme matching project name (case-insensitive) | |
| local project_name_lower=$(echo "$project_name" | tr '[:upper:]' '[:lower:]') | |
| for scheme in "${main_schemes[@]}"; do | |
| local scheme_lower=$(echo "$scheme" | tr '[:upper:]' '[:lower:]') | |
| if [[ "$scheme_lower" == "$project_name_lower" ]]; then | |
| echo "$scheme" | |
| return 0 | |
| fi | |
| done | |
| # Otherwise use first main scheme found | |
| echo "${main_schemes[0]}" | |
| return 0 | |
| fi | |
| fi | |
| fi | |
| fi | |
| # =================================================================== | |
| # TIER 2: Parse xcodebuild -list and filter patterns | |
| # =================================================================== | |
| local all_schemes=$(xcodebuild "$project_flag" "$project_path" -list 2>/dev/null | \ | |
| awk '/Schemes:/,/^$/' | \ | |
| grep -v "Schemes:" | \ | |
| grep -v "^$" | \ | |
| sed 's/^[[:space:]]*//') | |
| if [ -n "$all_schemes" ]; then | |
| # Try exact project name match first (case-insensitive) | |
| local exact_match=$(echo "$all_schemes" | grep -i "^${project_name}$" | head -1) | |
| if [ -n "$exact_match" ]; then | |
| echo "$exact_match" | |
| return 0 | |
| fi | |
| # Filter out known package/test/extension patterns | |
| local filtered_schemes=$(echo "$all_schemes" | grep -Ev '(Package|Tests|UITests|Dynamic|Static|Extension|Widget|Watch)' | \ | |
| grep -Ev '^(Alamofire|Supabase|Auth|Storage|Realtime|PostgREST|Functions|GoogleSignIn|GoogleUtilities|GTMAppAuth|GTMSessionFetcher|Kingfisher|PostHog|AppAuth|AppCheck|Promises|FBLPromises)') | |
| # Prefer matching project name from filtered schemes | |
| if [ -n "$filtered_schemes" ]; then | |
| local filtered_match=$(echo "$filtered_schemes" | grep -i "^${project_name}$" | head -1) | |
| if [ -n "$filtered_match" ]; then | |
| echo "$filtered_match" | |
| return 0 | |
| fi | |
| # Return first filtered scheme | |
| local first_filtered=$(echo "$filtered_schemes" | head -1) | |
| if [ -n "$first_filtered" ]; then | |
| echo "$first_filtered" | |
| return 0 | |
| fi | |
| fi | |
| # =================================================================== | |
| # TIER 3: Last resort - return first scheme from all schemes | |
| # =================================================================== | |
| local first_scheme=$(echo "$all_schemes" | head -1) | |
| if [ -n "$first_scheme" ]; then | |
| echo "$first_scheme" | |
| return 0 | |
| fi | |
| fi | |
| return 1 | |
| } | |
| # Function to auto-detect bundle ID | |
| detect_bundle_id() { | |
| local project_path="$1" | |
| local scheme="$2" | |
| local project_flag="" | |
| if [[ "$project_path" == *.xcworkspace ]]; then | |
| project_flag="-workspace" | |
| else | |
| project_flag="-project" | |
| fi | |
| # Get bundle ID from build settings | |
| local bundle_id=$(xcodebuild "$project_flag" "$project_path" \ | |
| -scheme "$scheme" \ | |
| -showBuildSettings 2>/dev/null | \ | |
| grep "PRODUCT_BUNDLE_IDENTIFIER = " | \ | |
| grep -v "DERIVE" | \ | |
| head -1 | \ | |
| sed 's/.*= *//' | \ | |
| xargs) | |
| if [ -n "$bundle_id" ]; then | |
| echo "$bundle_id" | |
| return 0 | |
| fi | |
| return 1 | |
| } | |
| # Function to list available devices and simulators | |
| list_devices() { | |
| echo -e "${BLUE}π± Available Physical Devices:${NC}" | |
| PHYSICAL_DEVICES=$(xcrun devicectl list devices 2>/dev/null || echo "") | |
| if [ -n "$PHYSICAL_DEVICES" ]; then | |
| echo "$PHYSICAL_DEVICES" | |
| else | |
| echo " (No physical devices found)" | |
| fi | |
| echo "" | |
| echo -e "${BLUE}π± Available Simulators:${NC}" | |
| SIMULATORS=$(xcrun simctl list devices available | grep -E "iPhone|iPad" || echo "") | |
| if [ -n "$SIMULATORS" ]; then | |
| echo "$SIMULATORS" | |
| else | |
| echo " (No simulators found)" | |
| fi | |
| echo "" | |
| } | |
| # Function to find physical device UDID by name | |
| find_physical_device() { | |
| local device_name="$1" | |
| local device_list=$(xcrun devicectl list devices 2>/dev/null || echo "") | |
| if [ -z "$device_list" ]; then | |
| return 1 | |
| fi | |
| # Remove quotes and special characters for matching | |
| local search_name=$(echo "$device_name" | sed "s/'//g" | sed 's/"//g') | |
| # First try exact match (case insensitive) | |
| local device_line=$(echo "$device_list" | grep -i "^[[:space:]]*$search_name" | head -1) | |
| # If exact match didn't work, try AND logic: all words must be present | |
| if [ -z "$device_line" ]; then | |
| local words_found=true | |
| local candidate_lines="$device_list" | |
| for word in $search_name; do | |
| local clean_word=$(echo "$word" | sed "s/'//g" | sed 's/"//g') | |
| if [ -n "$clean_word" ]; then | |
| candidate_lines=$(echo "$candidate_lines" | grep -i "$clean_word") | |
| if [ -z "$candidate_lines" ]; then | |
| words_found=false | |
| break | |
| fi | |
| fi | |
| done | |
| if [ "$words_found" = true ] && [ -n "$candidate_lines" ]; then | |
| device_line=$(echo "$candidate_lines" | head -1) | |
| fi | |
| fi | |
| if [ -n "$device_line" ]; then | |
| # Try to extract UDID using awk (more reliable for table format) | |
| local udid=$(echo "$device_line" | awk '{for(i=1;i<=NF;i++) if($i ~ /^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$/) print $i}' | head -1) | |
| # Fallback: use grep pattern matching | |
| if [ -z "$udid" ]; then | |
| udid=$(echo "$device_line" | grep -oE "[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}" | head -1) | |
| fi | |
| if [ -n "$udid" ]; then | |
| echo "$udid" | |
| return 0 | |
| fi | |
| fi | |
| return 1 | |
| } | |
| # Function to find simulator UDID by name | |
| find_simulator() { | |
| local simulator_name="$1" | |
| local simulator_list=$(xcrun simctl list devices available 2>/dev/null | grep -i "$simulator_name" | head -1) | |
| if [ -n "$simulator_list" ]; then | |
| local udid=$(echo "$simulator_list" | grep -oE '[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}' | head -1) | |
| if [ -n "$udid" ]; then | |
| echo "$udid" | |
| return 0 | |
| fi | |
| fi | |
| return 1 | |
| } | |
| # Parse command-line arguments | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --help|-h) | |
| show_help | |
| exit 0 | |
| ;; | |
| --project) | |
| PROJECT_PATH="$2" | |
| shift 2 | |
| ;; | |
| --scheme) | |
| SCHEME="$2" | |
| shift 2 | |
| ;; | |
| --bundle-id) | |
| BUNDLE_ID="$2" | |
| shift 2 | |
| ;; | |
| --configuration) | |
| CONFIGURATION="$2" | |
| shift 2 | |
| ;; | |
| --simulator|-s) | |
| FORCE_TYPE="simulator" | |
| shift | |
| ;; | |
| --device|-d) | |
| FORCE_TYPE="device" | |
| shift | |
| ;; | |
| *) | |
| if [ -z "$DEVICE_NAME" ]; then | |
| DEVICE_NAME="$1" | |
| else | |
| echo -e "${RED}β Error: Multiple device names provided${NC}" | |
| echo -e "${YELLOW}Use --help for usage information${NC}" | |
| exit 1 | |
| fi | |
| shift | |
| ;; | |
| esac | |
| done | |
| # Print header | |
| echo -e "${BLUE}π Universal iOS Build and Launch Script${NC}" | |
| echo -e "${BLUE}==========================================${NC}\n" | |
| # Auto-detect project if not provided | |
| if [ -z "$PROJECT_PATH" ]; then | |
| echo -e "${CYAN}π Auto-detecting project...${NC}" | |
| PROJECT_PATH=$(detect_project) | |
| if [ -z "$PROJECT_PATH" ]; then | |
| echo -e "${RED}β Error: Could not find .xcodeproj or .xcworkspace in current directory${NC}" | |
| echo -e "${YELLOW}Please specify project path with --project option${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}β Found project: $(basename "$PROJECT_PATH")${NC}\n" | |
| else | |
| if [ ! -d "$PROJECT_PATH" ]; then | |
| echo -e "${RED}β Error: Project path does not exist: $PROJECT_PATH${NC}" | |
| exit 1 | |
| fi | |
| fi | |
| # Auto-detect scheme if not provided | |
| if [ -z "$SCHEME" ]; then | |
| echo -e "${CYAN}π Auto-detecting scheme...${NC}" | |
| SCHEME=$(detect_scheme "$PROJECT_PATH") | |
| if [ -z "$SCHEME" ]; then | |
| echo -e "${RED}β Error: Could not auto-detect scheme${NC}" | |
| echo -e "${YELLOW}Please specify scheme with --scheme option${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}β Found scheme: $SCHEME${NC}\n" | |
| fi | |
| # Auto-detect bundle ID if not provided | |
| if [ -z "$BUNDLE_ID" ]; then | |
| echo -e "${CYAN}π Auto-detecting bundle identifier...${NC}" | |
| BUNDLE_ID=$(detect_bundle_id "$PROJECT_PATH" "$SCHEME") | |
| if [ -z "$BUNDLE_ID" ]; then | |
| echo -e "${RED}β Error: Could not auto-detect bundle ID${NC}" | |
| echo -e "${YELLOW}Please specify bundle ID with --bundle-id option${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}β Found bundle ID: $BUNDLE_ID${NC}\n" | |
| fi | |
| # Get product name from project path | |
| PRODUCT_NAME=$(basename "$PROJECT_PATH" | sed 's/\.[^.]*$//') | |
| # If no device name provided, show list and exit | |
| if [ -z "$DEVICE_NAME" ]; then | |
| echo -e "${YELLOW}No device name provided.${NC}\n" | |
| list_devices | |
| echo -e "${YELLOW}Usage: $0 [OPTIONS] [device-name]${NC}" | |
| echo -e "${YELLOW}Use --help for more information${NC}" | |
| exit 0 | |
| fi | |
| # Determine project flag | |
| PROJECT_FLAG="" | |
| if [[ "$PROJECT_PATH" == *.xcworkspace ]]; then | |
| PROJECT_FLAG="-workspace" | |
| else | |
| PROJECT_FLAG="-project" | |
| fi | |
| # Step 1: Find device | |
| echo -e "${YELLOW}π± Step 1: Finding device...${NC}" | |
| DEVICE_UDID="" | |
| DEVICE_TYPE="" | |
| DESTINATION="" | |
| # Search based on force type flag | |
| if [ "$FORCE_TYPE" = "simulator" ]; then | |
| DEVICE_UDID=$(find_simulator "$DEVICE_NAME" 2>/dev/null || echo "") | |
| if [ -n "$DEVICE_UDID" ]; then | |
| DEVICE_TYPE="simulator" | |
| DESTINATION="platform=iOS Simulator,name=$DEVICE_NAME" | |
| echo -e "${GREEN}β Found simulator: $DEVICE_NAME (UDID: $DEVICE_UDID)${NC}\n" | |
| fi | |
| elif [ "$FORCE_TYPE" = "device" ]; then | |
| DEVICE_UDID=$(find_physical_device "$DEVICE_NAME" 2>/dev/null || echo "") | |
| if [ -n "$DEVICE_UDID" ]; then | |
| DEVICE_TYPE="physical" | |
| DESTINATION="generic/platform=iOS" | |
| echo -e "${GREEN}β Found physical device: $DEVICE_NAME (UDID: $DEVICE_UDID)${NC}\n" | |
| fi | |
| else | |
| # Auto-detect: try simulator first, then physical device | |
| DEVICE_UDID=$(find_simulator "$DEVICE_NAME" 2>/dev/null || echo "") | |
| if [ -n "$DEVICE_UDID" ]; then | |
| DEVICE_TYPE="simulator" | |
| DESTINATION="platform=iOS Simulator,name=$DEVICE_NAME" | |
| echo -e "${GREEN}β Found simulator: $DEVICE_NAME (UDID: $DEVICE_UDID)${NC}\n" | |
| else | |
| DEVICE_UDID=$(find_physical_device "$DEVICE_NAME" 2>/dev/null || echo "") | |
| if [ -n "$DEVICE_UDID" ]; then | |
| DEVICE_TYPE="physical" | |
| DESTINATION="generic/platform=iOS" | |
| echo -e "${GREEN}β Found physical device: $DEVICE_NAME (UDID: $DEVICE_UDID)${NC}\n" | |
| fi | |
| fi | |
| fi | |
| if [ -z "$DEVICE_UDID" ]; then | |
| if [ -n "$FORCE_TYPE" ]; then | |
| echo -e "${RED}β Error: Could not find $FORCE_TYPE '$DEVICE_NAME'${NC}\n" | |
| else | |
| echo -e "${RED}β Error: Could not find device '$DEVICE_NAME'${NC}\n" | |
| fi | |
| list_devices | |
| exit 1 | |
| fi | |
| # Step 2: Build for device/simulator | |
| echo -e "${YELLOW}π¨ Step 2: Building app...${NC}" | |
| if [ "$DEVICE_TYPE" = "simulator" ]; then | |
| SIMULATOR_STATE=$(xcrun simctl list devices | grep "$DEVICE_UDID" | grep -oE '(Booted|Shutdown)' || echo "Shutdown") | |
| if [ "$SIMULATOR_STATE" != "Booted" ]; then | |
| echo -e "${YELLOW}Booting simulator...${NC}" | |
| xcrun simctl boot "$DEVICE_UDID" 2>/dev/null || true | |
| fi | |
| fi | |
| if ! xcodebuild \ | |
| "$PROJECT_FLAG" "$PROJECT_PATH" \ | |
| -scheme "$SCHEME" \ | |
| -configuration "$CONFIGURATION" \ | |
| -destination "$DESTINATION" \ | |
| -derivedDataPath build \ | |
| build 2>&1; then | |
| echo -e "${RED}β Build failed!${NC}" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}β Build succeeded${NC}\n" | |
| # Step 3: Get app path | |
| echo -e "${YELLOW}π¦ Step 3: Locating app bundle...${NC}" | |
| APP_PATH="" | |
| BUILD_DIR="" | |
| if [ "$DEVICE_TYPE" = "physical" ]; then | |
| BUILD_DIR="$CONFIGURATION-iphoneos" | |
| else | |
| BUILD_DIR="$CONFIGURATION-iphonesimulator" | |
| fi | |
| # 1. Check local build directory (from -derivedDataPath build) | |
| LOCAL_BUILD="$WORKING_DIR/build/Build/Products/$BUILD_DIR/$PRODUCT_NAME.app" | |
| if [ -d "$LOCAL_BUILD" ]; then | |
| APP_PATH="$LOCAL_BUILD" | |
| fi | |
| # 2. Check DerivedData (default location) | |
| if [ ! -d "$APP_PATH" ]; then | |
| DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData" | |
| LATEST_BUILD=$(find "$DERIVED_DATA_PATH" -name "$PRODUCT_NAME.app" -type d -path "*/Build/Products/$BUILD_DIR/*" 2>/dev/null | sort -r | head -1) | |
| if [ -n "$LATEST_BUILD" ] && [ -d "$LATEST_BUILD" ]; then | |
| APP_PATH="$LATEST_BUILD" | |
| fi | |
| fi | |
| # 3. Try to get from build settings | |
| if [ ! -d "$APP_PATH" ]; then | |
| BUILD_SETTINGS=$(xcodebuild \ | |
| "$PROJECT_FLAG" "$PROJECT_PATH" \ | |
| -scheme "$SCHEME" \ | |
| -configuration "$CONFIGURATION" \ | |
| -destination "$DESTINATION" \ | |
| -showBuildSettings 2>/dev/null) | |
| BUILD_PRODUCTS_DIR=$(echo "$BUILD_SETTINGS" | grep "BUILD_PRODUCTS_DIR" | head -1 | sed 's/.*= *//' | xargs) | |
| APP_NAME=$(echo "$BUILD_SETTINGS" | grep "PRODUCT_NAME" | head -1 | sed 's/.*= *//' | xargs) | |
| if [ -z "$APP_NAME" ]; then | |
| APP_NAME="$PRODUCT_NAME" | |
| fi | |
| if [ -n "$BUILD_PRODUCTS_DIR" ] && [ -d "$BUILD_PRODUCTS_DIR/$APP_NAME.app" ]; then | |
| APP_PATH="$BUILD_PRODUCTS_DIR/$APP_NAME.app" | |
| fi | |
| fi | |
| # 4. Last resort: search for app in build directory | |
| if [ ! -d "$APP_PATH" ]; then | |
| FOUND_APP=$(find "$WORKING_DIR/build" -name "$PRODUCT_NAME.app" -type d 2>/dev/null | head -1) | |
| if [ -n "$FOUND_APP" ] && [ -d "$FOUND_APP" ]; then | |
| APP_PATH="$FOUND_APP" | |
| fi | |
| fi | |
| if [ ! -d "$APP_PATH" ]; then | |
| echo -e "${RED}β Error: Could not locate app bundle${NC}" | |
| echo "Searched in:" | |
| echo " - $WORKING_DIR/build/Build/Products/$BUILD_DIR/$PRODUCT_NAME.app" | |
| echo " - DerivedData (latest build)" | |
| echo " - Build settings BUILD_PRODUCTS_DIR" | |
| echo " - Any location in build directory" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}β Found app at: $APP_PATH${NC}\n" | |
| # Step 4: Install app | |
| echo -e "${YELLOW}π² Step 4: Installing app...${NC}" | |
| if [ "$DEVICE_TYPE" = "physical" ]; then | |
| if ! xcrun devicectl device install app \ | |
| --device "$DEVICE_UDID" \ | |
| "$APP_PATH" 2>&1; then | |
| echo -e "${RED}β Installation failed!${NC}" | |
| exit 1 | |
| fi | |
| else | |
| if ! xcrun simctl install "$DEVICE_UDID" "$APP_PATH" 2>&1; then | |
| echo -e "${RED}β Installation failed!${NC}" | |
| exit 1 | |
| fi | |
| fi | |
| echo -e "${GREEN}β App installed successfully${NC}\n" | |
| # Step 5: Launch app | |
| echo -e "${YELLOW}π Step 5: Launching app...${NC}" | |
| PROCESS_ID="unknown" | |
| if [ "$DEVICE_TYPE" = "physical" ]; then | |
| LAUNCH_OUTPUT=$(xcrun devicectl device process launch \ | |
| --device "$DEVICE_UDID" \ | |
| "$BUNDLE_ID" 2>&1) || { | |
| echo -e "${RED}β Launch failed!${NC}" | |
| echo "$LAUNCH_OUTPUT" | |
| exit 1 | |
| } | |
| PROCESS_ID=$(echo "$LAUNCH_OUTPUT" | grep -i "process" | grep -oE "[0-9]+" | head -1 || echo "unknown") | |
| else | |
| LAUNCH_OUTPUT=$(xcrun simctl launch "$DEVICE_UDID" "$BUNDLE_ID" 2>&1) || { | |
| echo -e "${RED}β Launch failed!${NC}" | |
| echo "$LAUNCH_OUTPUT" | |
| exit 1 | |
| } | |
| PROCESS_ID=$(echo "$LAUNCH_OUTPUT" | grep -oE '[0-9]+' | head -1 || echo "unknown") | |
| fi | |
| echo -e "${GREEN}β App launched successfully${NC}\n" | |
| # Summary | |
| echo -e "${BLUE}==========================================${NC}" | |
| echo -e "${GREEN}β¨ Summary${NC}" | |
| echo -e "${BLUE}==========================================${NC}" | |
| echo "Project: $(basename "$PROJECT_PATH")" | |
| echo "Scheme: $SCHEME" | |
| echo "Configuration: $CONFIGURATION" | |
| echo "Bundle ID: $BUNDLE_ID" | |
| echo "Device Type: $DEVICE_TYPE" | |
| echo "Device: $DEVICE_NAME" | |
| echo "UDID: $DEVICE_UDID" | |
| echo "Process ID: $PROCESS_ID" | |
| echo -e "${BLUE}==========================================${NC}\n" | |
| if [ "$DEVICE_TYPE" = "physical" ]; then | |
| echo -e "${GREEN}π Done! The app should now be running on your device.${NC}" | |
| else | |
| echo -e "${GREEN}π Done! The app should now be running on the simulator.${NC}" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment