#!/usr/bin/env bash # # build-ios-client.sh — build the iOS SwiftUI client (VoiceCatiOS.app) for the # iOS simulator and stage it into dist/ios-client/. # # Two steps: # 1. Build VoiceCatCore.xcframework with all 3 slices (macOS + iOS device + # iOS simulator) via clients/apple/scripts/build-xcframework.sh --all. # The XCFramework must include the ios-arm64-simulator slice so the Swift # Package binary target resolves when xcodebuild compiles the iOS app. # 2. xcodebuild the VoiceCatiOS target against the iOS Simulator SDK, with # SYMROOT and OBJROOT pointed at the same directory so the Swift Package # and the app target find each other's build products. # # Usage: # scripts/build-ios-client.sh # Debug sim build into ./dist # scripts/build-ios-client.sh --dist /out # scripts/build-ios-client.sh --no-configure # skip cmake configure (xcframework step) # scripts/build-ios-client.sh --config Debug # Xcode config (default Debug) # scripts/build-ios-client.sh -h|--help # # Output: # clients/apple/iOS/build/Debug-iphonesimulator/VoiceCatiOS.app # dist/ios-client/VoiceCatiOS.app # # To install and launch on a simulator after building, use: # scripts/run-ios-simulator.sh # # macOS only. Requires VCPKG_ROOT and Xcode (with iOS Simulator runtime installed). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" VC_SCRIPT_NAME="build-ios-client" source "$SCRIPT_DIR/common.sh" DO_CONFIGURE=true XCODE_CONFIG="Debug" while [[ $# -gt 0 ]]; do case "$1" in --dist) vc_set_dist "$2"; shift 2 ;; --no-configure) DO_CONFIGURE=false; shift ;; --config) XCODE_CONFIG="$2"; shift 2 ;; -h|--help) vc_print_help "$0"; exit 0 ;; *) vc_die "unknown arg: $1 (try --help)" ;; esac done vc_require_macos vc_log "dist dir: $VC_DIST_DIR config: $XCODE_CONFIG" # ── Step 1: XCFramework (all slices) ────────────────────────────────────────── # The iOS simulator build requires the ios-arm64-simulator slice, so we always # build all three slices. --no-configure skips cmake configure but still runs # cmake --build (which is fast with a warm build cache). vc_step "build VoiceCatCore.xcframework (all slices: macOS + iOS device + iOS sim)" XCFW_ARGS=( --all ) $DO_CONFIGURE || XCFW_ARGS+=( --no-configure ) "$VC_REPO_ROOT/clients/apple/scripts/build-xcframework.sh" "${XCFW_ARGS[@]}" XCFW="$VC_REPO_ROOT/clients/apple/VoiceCatCore.xcframework" [[ -d "$XCFW" ]] || vc_die "xcframework not found at $XCFW" vc_ok "xcframework ready -> $XCFW" # ── Step 2: xcodebuild for iOS Simulator ────────────────────────────────────── # We use -target (not -scheme + -destination) to avoid the requirement of a # matching simulator runtime in xcrun simctl. When using -target, xcodebuild # skips destination resolution and builds directly against the requested SDK. # # SYMROOT and OBJROOT are both pinned to the same directory so the Swift Package # (VoiceCatCore) and the app target (VoiceCatiOS) resolve each other's products. # Without this, the package builds to clients/apple/build/ while the app target # looks in clients/apple/iOS/build/ — the module import fails with "unable to # resolve module dependency: 'VoiceCatCore'". # # CODE_SIGNING_ALLOWED=NO avoids provisioning-profile errors for local sim builds. XCODEPROJ="$VC_REPO_ROOT/clients/apple/iOS/VoiceCatiOS.xcodeproj" BUILD_DIR="$VC_REPO_ROOT/clients/apple/iOS/build" SIM_SDK_VER="$(xcrun --sdk iphonesimulator --show-sdk-version 2>/dev/null)" SIM_SDK="iphonesimulator${SIM_SDK_VER}" vc_step "xcodebuild VoiceCatiOS ($XCODE_CONFIG / $SIM_SDK)" xcodebuild \ -project "$XCODEPROJ" \ -target VoiceCatiOS \ -sdk "$SIM_SDK" \ -configuration "$XCODE_CONFIG" \ CODE_SIGNING_ALLOWED=NO \ ARCHS=arm64 \ ONLY_ACTIVE_ARCH=YES \ SYMROOT="$BUILD_DIR" \ OBJROOT="$BUILD_DIR" \ build APP="$BUILD_DIR/${XCODE_CONFIG}-iphonesimulator/VoiceCatiOS.app" [[ -d "$APP" ]] || vc_die "VoiceCatiOS.app not found at $APP" vc_ok "built -> $APP" # ── Stage ───────────────────────────────────────────────────────────────────── vc_step "stage -> $VC_DIST_DIR/ios-client" STAGE="$(vc_dist_subdir ios-client)" cp -R "$APP" "$STAGE/" vc_ok "VoiceCatiOS.app -> $STAGE/ ($(du -sh "$APP" | cut -f1))" vc_ok "done -> $STAGE"