forked from trent_larson/crowd-funder-for-time-pwa
Add full iOS build system: script, npm integration, and documentation
- Implement scripts/build-ios.sh with dev/test/prod, IPA, deploy, and Xcode support - Integrate all iOS build and legacy scripts into package.json (including deploy) - Update docs/ios-build-scripts.md: mark as complete, add usage and status - Update README.md: add iOS to quick start, platform builds, and docs links - Ensure iOS build system matches Android/Electron pattern for consistency
This commit is contained in:
@@ -1,37 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# build-ios.sh
|
||||
# Author: Matthew Raymer
|
||||
# Description: iOS build script for TimeSafari application
|
||||
# This script handles the complete iOS build process including cleanup,
|
||||
# web build, Capacitor build, asset generation, version management, and Xcode launch.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - macOS with Xcode installed
|
||||
# - iOS development certificates configured
|
||||
# - Capacitor dependencies installed
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/build-ios.sh # Standard build and open Xcode
|
||||
# ./scripts/build-ios.sh --version 1.0.3 # Build with specific version
|
||||
# ./scripts/build-ios.sh --build-number 35 # Build with specific build number
|
||||
# ./scripts/build-ios.sh --no-xcode # Build without opening Xcode
|
||||
# ./scripts/build-ios.sh --help # Show help
|
||||
# ./scripts/build-ios.sh --verbose # Enable verbose logging
|
||||
#
|
||||
# NPM Script Equivalents:
|
||||
# npm run build:ios # Standard iOS build
|
||||
# npm run build:ios:release # Release build with version bump
|
||||
#
|
||||
# Exit Codes:
|
||||
# 1 - iOS cleanup failed
|
||||
# 2 - Web build failed
|
||||
# 3 - Capacitor build failed
|
||||
# 4 - Capacitor sync failed
|
||||
# 5 - Asset generation failed
|
||||
# 6 - Version update failed
|
||||
# 7 - Xcode project opening failed
|
||||
# 8 - Ruby/Gem environment setup failed
|
||||
# 9 - iOS directory structure validation failed
|
||||
# Date: 2025-07-11
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
@@ -40,161 +12,242 @@ set -e
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
# Default values
|
||||
VERSION=""
|
||||
BUILD_NUMBER=""
|
||||
OPEN_XCODE=true
|
||||
MARKETING_VERSION=""
|
||||
BUILD_MODE="development"
|
||||
BUILD_TYPE="debug"
|
||||
OPEN_STUDIO=false
|
||||
BUILD_IPA=false
|
||||
BUILD_APP=false
|
||||
CLEAN_ONLY=false
|
||||
SYNC_ONLY=false
|
||||
ASSETS_ONLY=false
|
||||
DEPLOY_APP=false
|
||||
|
||||
# Function to show usage
|
||||
show_usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--version VERSION Set marketing version (e.g., 1.0.3)
|
||||
--build-number NUMBER Set build number (e.g., 35)
|
||||
--marketing-version VER Set marketing version explicitly
|
||||
--no-xcode Skip opening Xcode after build
|
||||
--help Show this help message
|
||||
--verbose Enable verbose logging
|
||||
--debug Enable debug mode
|
||||
|
||||
EXAMPLES:
|
||||
$0 # Standard build
|
||||
$0 --version 1.0.3 --build-number 35 # Build with specific version
|
||||
$0 --no-xcode # Build without opening Xcode
|
||||
$0 --verbose # Build with verbose output
|
||||
|
||||
EOF
|
||||
# Function to print iOS-specific usage
|
||||
print_ios_usage() {
|
||||
echo "Usage: $0 [options]"
|
||||
echo ""
|
||||
echo "iOS Build Options:"
|
||||
echo " --dev, --development Build for development environment"
|
||||
echo " --test Build for testing environment"
|
||||
echo " --prod, --production Build for production environment"
|
||||
echo " --debug Build debug app (default)"
|
||||
echo " --release Build release app"
|
||||
echo " --studio Open Xcode after build"
|
||||
echo " --ipa Build IPA file"
|
||||
echo " --app Build app bundle"
|
||||
echo " --clean Clean build artifacts only"
|
||||
echo " --sync Sync Capacitor only"
|
||||
echo " --assets Generate assets only"
|
||||
echo " --deploy Deploy app to connected device"
|
||||
echo ""
|
||||
echo "Common Options:"
|
||||
echo " -h, --help Show this help message"
|
||||
echo " -v, --verbose Enable verbose logging"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 --dev --studio # Development build + open Xcode"
|
||||
echo " $0 --prod --ipa # Production IPA build"
|
||||
echo " $0 --test --app # Testing app build"
|
||||
echo " $0 --clean # Clean only"
|
||||
echo " $0 --sync # Sync only"
|
||||
echo " $0 --deploy # Build and deploy to device"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
# Function to parse iOS-specific arguments
|
||||
parse_ios_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--version)
|
||||
VERSION="$2"
|
||||
MARKETING_VERSION="$2"
|
||||
shift 2
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--dev|--development)
|
||||
BUILD_MODE="development"
|
||||
;;
|
||||
--build-number)
|
||||
BUILD_NUMBER="$2"
|
||||
shift 2
|
||||
--test)
|
||||
BUILD_MODE="test"
|
||||
;;
|
||||
--marketing-version)
|
||||
MARKETING_VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-xcode)
|
||||
OPEN_XCODE=false
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
--verbose)
|
||||
VERBOSE=true
|
||||
shift
|
||||
--prod|--production)
|
||||
BUILD_MODE="production"
|
||||
;;
|
||||
--debug)
|
||||
DEBUG=true
|
||||
BUILD_TYPE="debug"
|
||||
;;
|
||||
--release)
|
||||
BUILD_TYPE="release"
|
||||
;;
|
||||
--studio)
|
||||
OPEN_STUDIO=true
|
||||
;;
|
||||
--ipa)
|
||||
BUILD_IPA=true
|
||||
;;
|
||||
--app)
|
||||
BUILD_APP=true
|
||||
;;
|
||||
--clean)
|
||||
CLEAN_ONLY=true
|
||||
;;
|
||||
--sync)
|
||||
SYNC_ONLY=true
|
||||
;;
|
||||
--assets)
|
||||
ASSETS_ONLY=true
|
||||
;;
|
||||
--deploy)
|
||||
DEPLOY_APP=true
|
||||
;;
|
||||
-h|--help)
|
||||
print_ios_usage
|
||||
exit 0
|
||||
;;
|
||||
-v|--verbose)
|
||||
set -x
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
log_warn "Unknown option: $1"
|
||||
shift
|
||||
log_warn "Unknown argument: $arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Function to validate iOS build environment
|
||||
# Function to validate iOS environment
|
||||
validate_ios_environment() {
|
||||
log_info "Validating iOS build environment..."
|
||||
|
||||
# Check if running on macOS
|
||||
if [[ "$(uname)" != "Darwin" ]]; then
|
||||
log_error "iOS builds require macOS"
|
||||
exit 9
|
||||
fi
|
||||
|
||||
# Check if Xcode is installed
|
||||
# Check for Xcode
|
||||
if ! command -v xcodebuild &> /dev/null; then
|
||||
log_error "Xcode is not installed or not in PATH"
|
||||
exit 9
|
||||
log_error "Xcode not found. Please install Xcode and command line tools."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if iOS directory exists
|
||||
# Check for iOS Simulator
|
||||
if ! command -v xcrun &> /dev/null; then
|
||||
log_error "Xcode command line tools not found. Please install with: xcode-select --install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for Capacitor
|
||||
if ! command -v npx &> /dev/null; then
|
||||
log_error "npx not found. Please install Node.js and npm."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for iOS platform
|
||||
if [ ! -d "ios" ]; then
|
||||
log_error "iOS directory not found. Run 'npx cap add ios' first."
|
||||
exit 9
|
||||
log_error "iOS platform not found. Please run: npx cap add ios"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "iOS build environment validated"
|
||||
}
|
||||
|
||||
# Function to setup Ruby/Gem environment for Capacitor
|
||||
setup_ruby_environment() {
|
||||
log_info "Setting up Ruby/Gem environment..."
|
||||
# Function to check iOS resources
|
||||
check_ios_resources() {
|
||||
log_info "Checking iOS resources..."
|
||||
|
||||
# Check if we're in a pkgx environment and setup gem paths
|
||||
if command -v gem &> /dev/null; then
|
||||
gem_path=$(which gem)
|
||||
if [[ "$gem_path" == *"pkgx"* ]]; then
|
||||
log_info "Detected pkgx environment, setting up gem paths..."
|
||||
shortened_path="${gem_path%/*/*}"
|
||||
export GEM_HOME="$shortened_path"
|
||||
export GEM_PATH="$shortened_path"
|
||||
log_info "GEM_HOME set to: $GEM_HOME"
|
||||
fi
|
||||
else
|
||||
log_error "Ruby gem command not found"
|
||||
exit 8
|
||||
# Check for required assets
|
||||
if [ ! -f "assets/icon.png" ]; then
|
||||
log_warning "App icon not found at assets/icon.png"
|
||||
fi
|
||||
|
||||
log_success "Ruby/Gem environment configured"
|
||||
}
|
||||
|
||||
# Function to setup iOS asset directories
|
||||
setup_ios_asset_directories() {
|
||||
log_info "Setting up iOS asset directories..."
|
||||
|
||||
# Create required asset directories that capacitor-assets expects
|
||||
mkdir -p "ios/App/App/Assets.xcassets/AppIcon.appiconset"
|
||||
echo '{"images":[]}' > "ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json"
|
||||
|
||||
mkdir -p "ios/App/App/Assets.xcassets/Splash.imageset"
|
||||
echo '{"images":[]}' > "ios/App/App/Assets.xcassets/Splash.imageset/Contents.json"
|
||||
|
||||
log_success "iOS asset directories prepared"
|
||||
}
|
||||
|
||||
# Function to update iOS version numbers
|
||||
update_ios_version() {
|
||||
if [ -n "$BUILD_NUMBER" ] || [ -n "$MARKETING_VERSION" ]; then
|
||||
log_info "Updating iOS version information..."
|
||||
|
||||
cd ios/App
|
||||
|
||||
# Update build number if provided
|
||||
if [ -n "$BUILD_NUMBER" ]; then
|
||||
log_info "Setting build number to: $BUILD_NUMBER"
|
||||
safe_execute "Updating build number" "xcrun agvtool new-version $BUILD_NUMBER" || exit 6
|
||||
fi
|
||||
|
||||
# Update marketing version if provided
|
||||
if [ -n "$MARKETING_VERSION" ]; then
|
||||
log_info "Setting marketing version to: $MARKETING_VERSION"
|
||||
safe_execute "Updating marketing version" "perl -p -i -e 's/MARKETING_VERSION = .*/MARKETING_VERSION = $MARKETING_VERSION;/g' App.xcodeproj/project.pbxproj" || exit 6
|
||||
fi
|
||||
|
||||
cd ../..
|
||||
log_success "iOS version information updated"
|
||||
else
|
||||
log_info "No version updates requested"
|
||||
if [ ! -f "assets/splash.png" ]; then
|
||||
log_warning "Splash screen not found at assets/splash.png"
|
||||
fi
|
||||
|
||||
# Check for iOS-specific files
|
||||
if [ ! -f "ios/App/App/Info.plist" ]; then
|
||||
log_warning "Info.plist not found"
|
||||
fi
|
||||
|
||||
if [ ! -f "ios/App/App/AppDelegate.swift" ]; then
|
||||
log_warning "AppDelegate.swift not found"
|
||||
fi
|
||||
|
||||
log_success "iOS resource check completed"
|
||||
}
|
||||
|
||||
# Function to clean iOS build
|
||||
clean_ios_build() {
|
||||
log_info "Cleaning iOS build artifacts..."
|
||||
|
||||
# Clean Xcode build
|
||||
if [ -d "ios/App/build" ]; then
|
||||
rm -rf ios/App/build/
|
||||
log_debug "Cleaned ios/App/build/"
|
||||
fi
|
||||
|
||||
# Clean DerivedData
|
||||
if [ -d "ios/App/DerivedData" ]; then
|
||||
rm -rf ios/App/DerivedData/
|
||||
log_debug "Cleaned ios/App/DerivedData/"
|
||||
fi
|
||||
|
||||
# Clean Capacitor
|
||||
npx cap clean ios || true
|
||||
|
||||
log_success "iOS build cleaned"
|
||||
}
|
||||
|
||||
# Function to build iOS app
|
||||
build_ios_app() {
|
||||
local build_config=""
|
||||
local scheme="App"
|
||||
local destination=""
|
||||
|
||||
if [ "$BUILD_TYPE" = "debug" ]; then
|
||||
build_config="Debug"
|
||||
destination="platform=iOS Simulator,name=iPhone 15 Pro"
|
||||
else
|
||||
build_config="Release"
|
||||
destination="platform=iOS,id=auto"
|
||||
fi
|
||||
|
||||
log_info "Building iOS app (${build_config})..."
|
||||
|
||||
cd ios/App
|
||||
|
||||
# Build the app
|
||||
xcodebuild -workspace App.xcworkspace \
|
||||
-scheme "$scheme" \
|
||||
-configuration "$build_config" \
|
||||
-destination "$destination" \
|
||||
build \
|
||||
CODE_SIGN_IDENTITY="" \
|
||||
CODE_SIGNING_REQUIRED=NO \
|
||||
CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
cd ../..
|
||||
|
||||
log_success "iOS app built successfully"
|
||||
}
|
||||
|
||||
# Function to deploy to device
|
||||
deploy_ios_app() {
|
||||
log_info "Deploy-app mode: building app and deploying to device"
|
||||
|
||||
# Check for connected device
|
||||
local devices=$(xcrun devicectl list devices --json | grep -c '"state":"booted"' || echo "0")
|
||||
if [ "$devices" -eq 0 ]; then
|
||||
log_error "No iOS device connected. Please connect a device and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build app for device
|
||||
BUILD_TYPE="debug"
|
||||
build_ios_app
|
||||
|
||||
# Get device ID
|
||||
local device_id=$(xcrun devicectl list devices --json | grep -o '"identifier":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||
|
||||
if [ -z "$device_id" ]; then
|
||||
log_error "Could not find device ID. Please ensure device is connected and unlocked."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install app on device
|
||||
log_info "Installing app on device..."
|
||||
xcrun devicectl device install app --device "$device_id" ios/App/build/Debug-iphoneos/App.app
|
||||
|
||||
log_success "iOS app deployed successfully to device!"
|
||||
log_info "You can now run the app with: npx cap run ios"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
@@ -203,9 +256,8 @@ parse_ios_args "$@"
|
||||
# Print build header
|
||||
print_header "TimeSafari iOS Build Process"
|
||||
log_info "Starting iOS build process at $(date)"
|
||||
|
||||
# Validate iOS build environment
|
||||
validate_ios_environment
|
||||
log_info "Build mode: $BUILD_MODE"
|
||||
log_info "Build type: $BUILD_TYPE"
|
||||
|
||||
# Setup environment for Capacitor build
|
||||
setup_build_env "capacitor"
|
||||
@@ -216,57 +268,114 @@ setup_app_directories
|
||||
# Load environment from .env file if it exists
|
||||
load_env_file ".env"
|
||||
|
||||
# Setup Ruby/Gem environment
|
||||
setup_ruby_environment
|
||||
# Validate iOS environment
|
||||
validate_ios_environment
|
||||
|
||||
# Step 1: Clean iOS app
|
||||
safe_execute "Cleaning iOS app" "npm run clean:ios || true" || exit 1
|
||||
# Handle clean-only mode
|
||||
if [ "$CLEAN_ONLY" = true ]; then
|
||||
log_info "Clean-only mode: cleaning build artifacts"
|
||||
clean_ios_build
|
||||
safe_execute "Cleaning dist directory" "clean_build_artifacts dist" || exit 1
|
||||
log_success "Clean completed successfully!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 2: Clean dist directory
|
||||
# Handle sync-only mode
|
||||
if [ "$SYNC_ONLY" = true ]; then
|
||||
log_info "Sync-only mode: syncing with Capacitor"
|
||||
safe_execute "Syncing with Capacitor" "npx cap sync ios" || exit 6
|
||||
log_success "Sync completed successfully!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Handle assets-only mode
|
||||
if [ "$ASSETS_ONLY" = true ]; then
|
||||
log_info "Assets-only mode: generating assets"
|
||||
safe_execute "Generating assets" "npx capacitor-assets generate --ios" || exit 7
|
||||
log_success "Assets generation completed successfully!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Handle deploy-app mode
|
||||
if [ "$DEPLOY_APP" = true ]; then
|
||||
deploy_ios_app
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 1: Check iOS resources
|
||||
check_ios_resources
|
||||
|
||||
# Step 2: Clean iOS build
|
||||
safe_execute "Cleaning iOS build" "clean_ios_build" || exit 1
|
||||
|
||||
# Step 3: Clean dist directory
|
||||
log_info "Cleaning dist directory..."
|
||||
clean_build_artifacts "dist"
|
||||
|
||||
# Step 3: Build web assets
|
||||
safe_execute "Building web assets" "npm run build:web" || exit 2
|
||||
|
||||
# Step 4: Build Capacitor version
|
||||
safe_execute "Building Capacitor version" "npm run build:capacitor" || exit 3
|
||||
# Step 4: Build Capacitor version with mode
|
||||
if [ "$BUILD_MODE" = "development" ]; then
|
||||
safe_execute "Building Capacitor version (development)" "npm run build:capacitor" || exit 3
|
||||
elif [ "$BUILD_MODE" = "test" ]; then
|
||||
safe_execute "Building Capacitor version (test)" "npm run build:capacitor -- --mode test" || exit 3
|
||||
elif [ "$BUILD_MODE" = "production" ]; then
|
||||
safe_execute "Building Capacitor version (production)" "npm run build:capacitor -- --mode production" || exit 3
|
||||
fi
|
||||
|
||||
# Step 5: Sync with Capacitor
|
||||
safe_execute "Syncing with Capacitor" "npx cap sync ios" || exit 4
|
||||
safe_execute "Syncing with Capacitor" "npx cap sync ios" || exit 6
|
||||
|
||||
# Step 6: Setup iOS asset directories
|
||||
setup_ios_asset_directories
|
||||
# Step 6: Generate assets
|
||||
safe_execute "Generating assets" "npx capacitor-assets generate --ios" || exit 7
|
||||
|
||||
# Step 7: Generate iOS assets
|
||||
safe_execute "Generating iOS assets" "npx capacitor-assets generate --ios" || exit 5
|
||||
# Step 7: Build iOS app
|
||||
safe_execute "Building iOS app" "build_ios_app" || exit 5
|
||||
|
||||
# Step 8: Update version information
|
||||
update_ios_version
|
||||
# Step 8: Build IPA/App if requested
|
||||
if [ "$BUILD_IPA" = true ]; then
|
||||
log_info "Building IPA package..."
|
||||
cd ios/App
|
||||
xcodebuild -workspace App.xcworkspace \
|
||||
-scheme App \
|
||||
-configuration Release \
|
||||
-archivePath build/App.xcarchive \
|
||||
archive \
|
||||
CODE_SIGN_IDENTITY="" \
|
||||
CODE_SIGNING_REQUIRED=NO \
|
||||
CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath build/App.xcarchive \
|
||||
-exportPath build/ \
|
||||
-exportOptionsPlist exportOptions.plist
|
||||
cd ../..
|
||||
log_success "IPA package built successfully"
|
||||
fi
|
||||
|
||||
# Step 9: Open Xcode (if requested)
|
||||
if [ "$OPEN_XCODE" = true ]; then
|
||||
safe_execute "Opening Xcode" "npx cap open ios" || exit 7
|
||||
log_info "Xcode opened. You can now build and run on simulator or device."
|
||||
log_info "Next steps in Xcode:"
|
||||
log_info " 1. Select Product -> Destination with a Simulator version"
|
||||
log_info " 2. Click the run arrow to build and test"
|
||||
log_info " 3. For release: Choose Product -> Destination -> Any iOS Device"
|
||||
log_info " 4. For release: Choose Product -> Archive"
|
||||
else
|
||||
log_info "Skipping Xcode opening as requested"
|
||||
if [ "$BUILD_APP" = true ]; then
|
||||
log_info "Building app bundle..."
|
||||
# App bundle is already built in step 7
|
||||
log_success "App bundle built successfully"
|
||||
fi
|
||||
|
||||
# Step 9: Open Xcode if requested
|
||||
if [ "$OPEN_STUDIO" = true ]; then
|
||||
safe_execute "Opening Xcode" "npx cap open ios" || exit 8
|
||||
fi
|
||||
|
||||
# Print build summary
|
||||
log_success "iOS build completed successfully!"
|
||||
|
||||
if [ -n "$BUILD_NUMBER" ] || [ -n "$MARKETING_VERSION" ]; then
|
||||
log_info "Version Information:"
|
||||
[ -n "$BUILD_NUMBER" ] && log_info " Build Number: $BUILD_NUMBER"
|
||||
[ -n "$MARKETING_VERSION" ] && log_info " Marketing Version: $MARKETING_VERSION"
|
||||
log_info "Build mode: $BUILD_MODE"
|
||||
log_info "Build type: $BUILD_TYPE"
|
||||
if [ "$BUILD_IPA" = true ]; then
|
||||
log_info "IPA build: completed"
|
||||
fi
|
||||
if [ "$BUILD_APP" = true ]; then
|
||||
log_info "App build: completed"
|
||||
fi
|
||||
if [ "$OPEN_STUDIO" = true ]; then
|
||||
log_info "Xcode: opened"
|
||||
fi
|
||||
|
||||
log_info "iOS project ready at: ios/App/"
|
||||
print_footer "iOS Build"
|
||||
|
||||
# Exit with success
|
||||
|
||||
Reference in New Issue
Block a user