Fixed iOS 13.0 compatibility issue in test harness by replacing Logger (iOS 14+) with os_log (iOS 13+). Fixed build script to correctly detect and sync Capacitor config from App subdirectory. Unified both Android and iOS test app UIs to use www/index.html as the canonical source. Changes: - DailyNotificationBackgroundTaskTestHarness: Replace Logger with os_log for iOS 13.0 deployment target compatibility - build-ios-test-app.sh: Fix Capacitor sync path detection to check both current directory and App/ subdirectory for config files - test-apps: Update both Android and iOS test apps to use www/index.html as the canonical UI source for consistency This ensures the plugin builds on iOS 13.0+ and both test apps provide the same testing experience across platforms.
502 lines
19 KiB
Bash
Executable File
502 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Exit on error
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
log_step() {
|
|
echo -e "${BLUE}[STEP]${NC} $1"
|
|
}
|
|
|
|
# Validation functions
|
|
check_command() {
|
|
if ! command -v $1 &> /dev/null; then
|
|
# Try rbenv shims for pod command
|
|
if [ "$1" = "pod" ] && [ -f "$HOME/.rbenv/shims/pod" ]; then
|
|
log_info "Found pod in rbenv shims"
|
|
return 0
|
|
fi
|
|
log_error "$1 is not installed. Please install it first."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Get pod command (handles rbenv)
|
|
get_pod_command() {
|
|
if command -v pod &> /dev/null; then
|
|
echo "pod"
|
|
elif [ -f "$HOME/.rbenv/shims/pod" ]; then
|
|
echo "$HOME/.rbenv/shims/pod"
|
|
else
|
|
log_error "CocoaPods (pod) not found. Please install CocoaPods first."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_environment() {
|
|
log_step "Checking environment..."
|
|
|
|
# Check for required tools
|
|
check_command "xcodebuild"
|
|
check_command "pod"
|
|
check_command "node"
|
|
check_command "npm"
|
|
|
|
# Check for Xcode
|
|
if ! xcodebuild -version &> /dev/null; then
|
|
log_error "Xcode is not installed or not properly configured"
|
|
exit 1
|
|
fi
|
|
|
|
# Check Node.js version
|
|
NODE_VERSION=$(node -v | cut -d. -f1 | tr -d 'v')
|
|
if [ "$NODE_VERSION" -lt 14 ]; then
|
|
log_error "Node.js version 14 or higher is required"
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Environment check passed"
|
|
}
|
|
|
|
# Parse arguments
|
|
TARGET="simulator"
|
|
BUILD_CONFIG="Debug"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--simulator)
|
|
TARGET="simulator"
|
|
shift
|
|
;;
|
|
--device)
|
|
TARGET="device"
|
|
shift
|
|
;;
|
|
--release)
|
|
BUILD_CONFIG="Release"
|
|
shift
|
|
;;
|
|
--help)
|
|
echo "Usage: $0 [--simulator|--device] [--release]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --simulator Build for iOS Simulator (default)"
|
|
echo " --device Build for physical device"
|
|
echo " --release Build Release configuration (default: Debug)"
|
|
echo " --help Show this help message"
|
|
exit 0
|
|
;;
|
|
*)
|
|
log_error "Unknown option: $1"
|
|
echo "Use --help for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Check if iOS test app exists
|
|
TEST_APP_DIR="test-apps/ios-test-app"
|
|
if [ ! -d "$TEST_APP_DIR" ]; then
|
|
log_error "iOS test app not found at $TEST_APP_DIR"
|
|
log_info "The iOS test app needs to be created first."
|
|
log_info "See doc/directives/0003-iOS-Android-Parity-Directive.md for requirements."
|
|
exit 1
|
|
fi
|
|
|
|
# Main build function
|
|
build_ios_test_app() {
|
|
log_step "Building iOS test app..."
|
|
|
|
# Navigate to iOS App directory (where workspace is located)
|
|
IOS_APP_DIR="$TEST_APP_DIR/ios/App"
|
|
if [ ! -d "$IOS_APP_DIR" ]; then
|
|
log_error "iOS App directory not found: $IOS_APP_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
cd "$IOS_APP_DIR" || exit 1
|
|
|
|
# Check for workspace or project (these are directories, not files)
|
|
if [ -d "App.xcworkspace" ]; then
|
|
WORKSPACE="App.xcworkspace"
|
|
SCHEME="App"
|
|
elif [ -d "App.xcodeproj" ]; then
|
|
PROJECT="App.xcodeproj"
|
|
SCHEME="App"
|
|
else
|
|
log_error "No Xcode workspace or project found in $IOS_APP_DIR"
|
|
log_info "Expected: App.xcworkspace or App.xcodeproj"
|
|
log_info "Found files: $(ls -la | head -10)"
|
|
exit 1
|
|
fi
|
|
|
|
# Install CocoaPods dependencies
|
|
log_step "Installing CocoaPods dependencies..."
|
|
POD_CMD=$(get_pod_command)
|
|
if [ -f "Podfile" ]; then
|
|
if ! $POD_CMD install; then
|
|
log_error "CocoaPods installation failed"
|
|
exit 1
|
|
fi
|
|
log_info "CocoaPods dependencies installed"
|
|
else
|
|
log_warn "No Podfile found, skipping pod install"
|
|
fi
|
|
|
|
# Build TypeScript/JavaScript if package.json exists
|
|
if [ -f "package.json" ]; then
|
|
log_step "Building web assets..."
|
|
if [ -f "package.json" ] && grep -q "\"build\"" package.json; then
|
|
if ! npm run build; then
|
|
log_error "Web assets build failed"
|
|
exit 1
|
|
fi
|
|
log_info "Web assets built"
|
|
fi
|
|
|
|
# Sync Capacitor if needed
|
|
# Check for config in current directory or App subdirectory
|
|
CAP_CONFIG_DIR=""
|
|
if [ -f "capacitor.config.ts" ] || [ -f "capacitor.config.json" ]; then
|
|
CAP_CONFIG_DIR="."
|
|
elif [ -f "App/capacitor.config.json" ] || [ -f "App/capacitor.config.ts" ]; then
|
|
CAP_CONFIG_DIR="App"
|
|
fi
|
|
|
|
if command -v npx &> /dev/null && [ -n "$CAP_CONFIG_DIR" ]; then
|
|
log_step "Syncing Capacitor..."
|
|
# Run sync from directory containing config
|
|
if [ "$CAP_CONFIG_DIR" != "." ]; then
|
|
cd "$CAP_CONFIG_DIR" || exit 1
|
|
fi
|
|
if ! npx cap sync ios; then
|
|
log_error "Capacitor sync failed"
|
|
exit 1
|
|
fi
|
|
# Return to ios/App directory if we changed
|
|
if [ "$CAP_CONFIG_DIR" != "." ]; then
|
|
cd .. || exit 1
|
|
fi
|
|
log_info "Capacitor synced"
|
|
fi
|
|
fi
|
|
|
|
# Determine SDK and destination
|
|
if [ "$TARGET" = "simulator" ]; then
|
|
SDK="iphonesimulator"
|
|
|
|
# Initialize simulator variables
|
|
SIMULATOR_ID=""
|
|
SIMULATOR_NAME=""
|
|
|
|
# Auto-detect available iPhone simulator using device ID (more reliable)
|
|
log_step "Detecting available iPhone simulator..."
|
|
SIMULATOR_LINE=$(xcrun simctl list devices available 2>&1 | grep -i "iPhone" | head -1)
|
|
|
|
if [ -n "$SIMULATOR_LINE" ]; then
|
|
# Extract device ID (UUID in parentheses)
|
|
SIMULATOR_ID=$(echo "$SIMULATOR_LINE" | sed -E 's/.*\(([A-F0-9-]+)\).*/\1/')
|
|
# Extract device name (everything before the first parenthesis)
|
|
SIMULATOR_NAME=$(echo "$SIMULATOR_LINE" | sed -E 's/^[[:space:]]*([^(]+).*/\1/' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
|
|
if [ -n "$SIMULATOR_ID" ] && [ "$SIMULATOR_ID" != "Shutdown" ] && [ "$SIMULATOR_ID" != "Booted" ]; then
|
|
# Use device ID (most reliable)
|
|
DESTINATION="platform=iOS Simulator,id=$SIMULATOR_ID"
|
|
log_info "Building for iOS Simulator ($SIMULATOR_NAME, ID: $SIMULATOR_ID)..."
|
|
elif [ -n "$SIMULATOR_NAME" ]; then
|
|
# Fallback to device name
|
|
DESTINATION="platform=iOS Simulator,name=$SIMULATOR_NAME"
|
|
log_info "Building for iOS Simulator ($SIMULATOR_NAME)..."
|
|
else
|
|
# Last resort: generic destination
|
|
DESTINATION="platform=iOS Simulator,name=Any iOS Simulator Device"
|
|
log_warn "Using generic simulator destination"
|
|
fi
|
|
else
|
|
# No iPhone simulators found, use generic
|
|
DESTINATION="platform=iOS Simulator,name=Any iOS Simulator Device"
|
|
log_warn "No iPhone simulators found, using generic destination"
|
|
fi
|
|
|
|
ARCHIVE_PATH="build/ios-test-app-simulator.xcarchive"
|
|
else
|
|
SDK="iphoneos"
|
|
DESTINATION="generic/platform=iOS"
|
|
ARCHIVE_PATH="build/ios-test-app-device.xcarchive"
|
|
fi
|
|
|
|
# Clean build folder
|
|
log_step "Cleaning build folder..."
|
|
if [ -n "$WORKSPACE" ]; then
|
|
xcodebuild clean -workspace "$WORKSPACE" -scheme "$SCHEME" -configuration "$BUILD_CONFIG" -sdk "$SDK" || true
|
|
else
|
|
xcodebuild clean -project "$PROJECT" -scheme "$SCHEME" -configuration "$BUILD_CONFIG" -sdk "$SDK" || true
|
|
fi
|
|
|
|
# Build
|
|
log_step "Building for $TARGET ($BUILD_CONFIG)..."
|
|
if [ -n "$WORKSPACE" ]; then
|
|
if ! xcodebuild build \
|
|
-workspace "$WORKSPACE" \
|
|
-scheme "$SCHEME" \
|
|
-configuration "$BUILD_CONFIG" \
|
|
-sdk "$SDK" \
|
|
-destination "$DESTINATION" \
|
|
CODE_SIGN_IDENTITY="" \
|
|
CODE_SIGNING_REQUIRED=NO \
|
|
CODE_SIGNING_ALLOWED=NO; then
|
|
log_error "Build failed"
|
|
exit 1
|
|
fi
|
|
else
|
|
if ! xcodebuild build \
|
|
-project "$PROJECT" \
|
|
-scheme "$SCHEME" \
|
|
-configuration "$BUILD_CONFIG" \
|
|
-sdk "$SDK" \
|
|
-destination "$DESTINATION" \
|
|
CODE_SIGN_IDENTITY="" \
|
|
CODE_SIGNING_REQUIRED=NO \
|
|
CODE_SIGNING_ALLOWED=NO; then
|
|
log_error "Build failed"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
log_info "Build successful!"
|
|
|
|
# Find the built app in DerivedData
|
|
if [ "$TARGET" = "simulator" ]; then
|
|
# Xcode builds to DerivedData, find the app there
|
|
DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData"
|
|
APP_PATH=$(find "$DERIVED_DATA_PATH" -name "App.app" -path "*/Build/Products/Debug-iphonesimulator/*" -type d 2>/dev/null | head -1)
|
|
|
|
if [ -n "$APP_PATH" ]; then
|
|
log_info "App built at: $APP_PATH"
|
|
log_info ""
|
|
|
|
# Boot simulator if not already booted
|
|
log_step "Checking simulator status..."
|
|
if [ -n "$SIMULATOR_ID" ]; then
|
|
SIMULATOR_STATE=$(xcrun simctl list devices | grep "$SIMULATOR_ID" | grep -o "(Booted\|Shutdown)" | head -1)
|
|
|
|
if [ "$SIMULATOR_STATE" != "Booted" ]; then
|
|
log_step "Booting simulator ($SIMULATOR_NAME)..."
|
|
xcrun simctl boot "$SIMULATOR_ID" 2>/dev/null || log_warn "Simulator may already be booting"
|
|
|
|
# Open Simulator app if not already open
|
|
if ! pgrep -x "Simulator" > /dev/null; then
|
|
log_step "Opening Simulator app..."
|
|
open -a Simulator
|
|
fi
|
|
|
|
# Wait for simulator to fully boot (up to 60 seconds)
|
|
log_step "Waiting for simulator to boot (this may take up to 60 seconds)..."
|
|
BOOT_TIMEOUT=60
|
|
ELAPSED=0
|
|
CURRENT_STATE="Shutdown"
|
|
while [ $ELAPSED -lt $BOOT_TIMEOUT ]; do
|
|
CURRENT_STATE=$(xcrun simctl list devices | grep "$SIMULATOR_ID" | grep -o "(Booted\|Shutdown)" | head -1)
|
|
if [ "$CURRENT_STATE" = "Booted" ]; then
|
|
log_info "Simulator booted successfully (took ${ELAPSED}s)"
|
|
# Give it a few more seconds to fully initialize
|
|
sleep 3
|
|
break
|
|
fi
|
|
if [ $((ELAPSED % 5)) -eq 0 ] && [ $ELAPSED -gt 0 ]; then
|
|
log_info "Still waiting... (${ELAPSED}s elapsed)"
|
|
fi
|
|
sleep 1
|
|
ELAPSED=$((ELAPSED + 1))
|
|
done
|
|
|
|
if [ "$CURRENT_STATE" != "Booted" ]; then
|
|
log_warn "Simulator may not have finished booting (waited ${ELAPSED}s)"
|
|
log_warn "You may need to manually boot the simulator and try again"
|
|
else
|
|
# Verify simulator is actually ready (not just booted)
|
|
log_info "Verifying simulator is ready..."
|
|
READY_ATTEMPTS=0
|
|
MAX_READY_ATTEMPTS=10
|
|
while [ $READY_ATTEMPTS -lt $MAX_READY_ATTEMPTS ]; do
|
|
# Try a simple command to verify simulator is responsive
|
|
if xcrun simctl list devices | grep "$SIMULATOR_ID" | grep -q "Booted"; then
|
|
# Try to get device info to verify it's responsive
|
|
if xcrun simctl get_app_container "$SIMULATOR_ID" com.apple.Preferences >/dev/null 2>&1; then
|
|
log_info "Simulator is ready"
|
|
break
|
|
fi
|
|
fi
|
|
sleep 1
|
|
READY_ATTEMPTS=$((READY_ATTEMPTS + 1))
|
|
done
|
|
if [ $READY_ATTEMPTS -eq $MAX_READY_ATTEMPTS ]; then
|
|
log_warn "Simulator may not be fully ready yet"
|
|
fi
|
|
fi
|
|
else
|
|
log_info "Simulator already booted"
|
|
# Verify it's actually ready
|
|
if ! xcrun simctl get_app_container "$SIMULATOR_ID" com.apple.Preferences >/dev/null 2>&1; then
|
|
log_warn "Simulator is booted but may not be fully ready"
|
|
log_info "Waiting a few seconds for simulator to be ready..."
|
|
sleep 5
|
|
fi
|
|
fi
|
|
|
|
# Install the app
|
|
log_step "Installing app on simulator..."
|
|
if xcrun simctl install "$SIMULATOR_ID" "$APP_PATH" 2>&1; then
|
|
log_info "App installed successfully"
|
|
else
|
|
log_warn "Install may have failed (app may already be installed)"
|
|
fi
|
|
|
|
# Wait a moment for install to complete
|
|
sleep 1
|
|
|
|
# Launch the app (try multiple methods)
|
|
log_step "Launching app..."
|
|
LAUNCH_SUCCESS=false
|
|
LAUNCH_ERROR=""
|
|
|
|
# Wait a moment for simulator to be fully ready
|
|
sleep 2
|
|
|
|
# Method 1: Direct launch (capture output to check for errors)
|
|
# Note: Bundle ID is com.timesafari.dailynotification (not .test)
|
|
log_info "Attempting to launch app..."
|
|
LAUNCH_OUTPUT=$(xcrun simctl launch "$SIMULATOR_ID" com.timesafari.dailynotification 2>&1)
|
|
LAUNCH_EXIT_CODE=$?
|
|
|
|
if [ $LAUNCH_EXIT_CODE -eq 0 ]; then
|
|
# Check if output contains process ID (successful launch)
|
|
# Format can be either "PID" or "bundle: PID"
|
|
if echo "$LAUNCH_OUTPUT" | grep -qE "^[0-9]+$|^[^:]+: [0-9]+$"; then
|
|
LAUNCH_SUCCESS=true
|
|
# Extract PID (either standalone number or after colon)
|
|
APP_PID=$(echo "$LAUNCH_OUTPUT" | sed -E 's/^[^:]*:? *([0-9]+).*/\1/' | head -1)
|
|
log_info "✅ App launched successfully! (PID: $APP_PID)"
|
|
else
|
|
# Launch command succeeded but may not have actually launched
|
|
log_warn "Launch command returned success but output unexpected: $LAUNCH_OUTPUT"
|
|
fi
|
|
else
|
|
# Capture error message
|
|
LAUNCH_ERROR="$LAUNCH_OUTPUT"
|
|
log_warn "Launch failed: $LAUNCH_ERROR"
|
|
fi
|
|
|
|
# Method 2: Verify app is actually running
|
|
if [ "$LAUNCH_SUCCESS" = false ]; then
|
|
log_info "Checking if app is already running..."
|
|
sleep 2
|
|
RUNNING_APPS=$(xcrun simctl listapps "$SIMULATOR_ID" 2>/dev/null | grep -A 5 "com.timesafari.dailynotification" || echo "")
|
|
|
|
if [ -n "$RUNNING_APPS" ]; then
|
|
log_info "App appears to be installed. Trying to verify it's running..."
|
|
# Try to get app state
|
|
APP_STATE=$(xcrun simctl listapps "$SIMULATOR_ID" 2>/dev/null | grep -A 10 "com.timesafari.dailynotification" | grep "ApplicationType" || echo "")
|
|
if [ -n "$APP_STATE" ]; then
|
|
log_info "App found in simulator. Attempting manual launch..."
|
|
# Try opening via Simulator app
|
|
open -a Simulator
|
|
sleep 1
|
|
# Try launch one more time
|
|
if xcrun simctl launch "$SIMULATOR_ID" com.timesafari.dailynotification >/dev/null 2>&1; then
|
|
LAUNCH_SUCCESS=true
|
|
log_info "✅ App launched successfully on retry!"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Final verification: check if app process is running
|
|
if [ "$LAUNCH_SUCCESS" = true ]; then
|
|
sleep 2
|
|
# Try to verify app is running by checking if we can get its container
|
|
if xcrun simctl get_app_container "$SIMULATOR_ID" com.timesafari.dailynotification >/dev/null 2>&1; then
|
|
log_info "✅ Verified: App is installed and accessible"
|
|
else
|
|
log_warn "⚠️ Launch reported success but app verification failed"
|
|
log_warn " The app may still be starting. Check the Simulator."
|
|
fi
|
|
else
|
|
log_warn "❌ Automatic launch failed"
|
|
log_info ""
|
|
log_info "The app is installed. To launch manually:"
|
|
log_info " 1. Open Simulator app (if not already open)"
|
|
log_info " 2. Find the app icon on the home screen and tap it"
|
|
log_info " 3. Or run: xcrun simctl launch $SIMULATOR_ID com.timesafari.dailynotification"
|
|
if [ -n "$LAUNCH_ERROR" ]; then
|
|
log_info ""
|
|
log_info "Launch error details:"
|
|
log_info " $LAUNCH_ERROR"
|
|
fi
|
|
fi
|
|
|
|
log_info ""
|
|
log_info "✅ Build and deployment complete!"
|
|
else
|
|
log_info ""
|
|
log_info "To run on simulator manually:"
|
|
log_info " xcrun simctl install booted \"$APP_PATH\""
|
|
log_info " xcrun simctl launch booted com.timesafari.dailynotification"
|
|
fi
|
|
else
|
|
log_warn "Could not find built app in DerivedData"
|
|
log_info "App was built successfully, but path detection failed."
|
|
log_info "You can find it in Xcode's DerivedData folder or run from Xcode directly."
|
|
fi
|
|
else
|
|
log_info ""
|
|
log_info "To install on device:"
|
|
log_info " Open App.xcworkspace in Xcode"
|
|
log_info " Select your device"
|
|
log_info " Press Cmd+R to build and run"
|
|
fi
|
|
|
|
cd - > /dev/null
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
log_info "iOS Test App Build Script"
|
|
log_info "Target: $TARGET | Configuration: $BUILD_CONFIG"
|
|
log_info ""
|
|
|
|
check_environment
|
|
|
|
# Get absolute path to repo root
|
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
REPO_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
|
|
cd "$REPO_ROOT"
|
|
|
|
build_ios_test_app
|
|
|
|
log_info ""
|
|
log_info "✅ Build complete!"
|
|
}
|
|
|
|
main "$@"
|
|
|