feat(ios): ship iOS SwiftUI client (VoiceCatiOS)
Full SwiftUI app at clients/apple/iOS/VoiceCatiOS.xcodeproj:
- 24 Swift source files: AppState + SessionState (@Observable @MainActor),
AudioSessionManager (AVAudioSession owner + interruption/route handling),
ServerListStore/SavedServer (App Group container + Keychain sharing),
and 14 SwiftUI views covering the full feature set
- NavigationSplitView on iPad, TabView on iPhone (horizontalSizeClass)
- Channel tree via OutlineGroup, user list with context menu admin actions
- PTT via DragGesture(minimumDistance: 0) + @GestureState
- onEvent closures hop to MainActor via Task { @MainActor in ... }
- App Group: group.cat.voice.VoiceCat (shared with future ReplayKit extension)
C ABI: add vc_audio_suspend / vc_audio_resume (AudioEngine::suspend/resume)
called by AudioSessionManager on AVAudioSession interruption events.
XCFramework: add ios-arm64 and ios-arm64-simulator slices to build-xcframework.sh;
Package.swift gains .iOS(.v17) platform; CMakePresets.json adds apple-ios /
apple-ios-sim presets with arm64-ios / arm64-ios-simulator vcpkg triplets.
Verified: xcodebuild -target VoiceCatiOS -sdk iphonesimulator26.5 BUILD SUCCEEDED.
This commit is contained in:
@@ -73,7 +73,7 @@ echo "[build-xcframework] VCPKG_ROOT=$VCPKG_ROOT"
|
||||
|
||||
# ── Build each requested slice ────────────────────────────────────────────────────
|
||||
build_slice() {
|
||||
local preset="$1" slice_name="$2"
|
||||
local preset="$1" slice_name="$2" vcpkg_triplet="$3"
|
||||
echo "[build-xcframework] === $slice_name: configure + build ($preset) ==="
|
||||
if $DO_CONFIGURE; then
|
||||
cmake --preset "$preset"
|
||||
@@ -97,25 +97,29 @@ build_slice() {
|
||||
# Without this, the final executable (test runner / app) would get undefined-symbol
|
||||
# linker errors for protobuf/mbedtls/sodium/opus/… symbols that libvoicecat.a references
|
||||
# but doesn't contain. See clients/apple/README.md "Fat static library" section.
|
||||
local vcpkg_libs_dir="$REPO_ROOT/build/$preset/vcpkg_installed"
|
||||
#
|
||||
# IMPORTANT: search only the TARGET triplet's lib dir, not all of vcpkg_installed/.
|
||||
# Cross-compile builds install a HOST triplet directory alongside the target (e.g.
|
||||
# arm64-osx/ next to arm64-ios/) — that host directory contains macOS binaries needed
|
||||
# to build protoc/etc at configure time. Merging those macOS .a files into the iOS
|
||||
# fat library triggers xcodebuild's "binaries with multiple platforms" rejection.
|
||||
local vcpkg_target_lib_dir="$REPO_ROOT/build/$preset/vcpkg_installed/$vcpkg_triplet/lib"
|
||||
local fat_lib="$REPO_ROOT/build/$preset/lib/libvoicecat-fat.a"
|
||||
echo "[build-xcframework] $slice_name: merging vcpkg deps into fat static lib"
|
||||
# Collect all .a files (libvoicecat.a + every vcpkg .a). libtool -static concatenates
|
||||
echo "[build-xcframework] $slice_name: merging vcpkg deps into fat static lib (triplet: $vcpkg_triplet)"
|
||||
# Collect all .a files (libvoicecat.a + every vcpkg target .a). libtool -static concatenates
|
||||
# object files from all input archives; duplicate-object warnings are benign (the linker
|
||||
# resolves duplicates at final link time). "no symbols" warnings are for empty AVX2/AVX512
|
||||
# objects on arm64 — also benign.
|
||||
local all_libs=( "$lib" )
|
||||
# Only release libs (arm64-osx/lib/*.a), NOT debug libs (arm64-osx/debug/lib/*.a) —
|
||||
# vcpkg installs both; the debug libs are ~10x larger and not needed for a Release build.
|
||||
while IFS= read -r f; do all_libs+=( "$f" ); done < <(find "$vcpkg_libs_dir" -name '*.a' -not -name 'libvoicecat*' -not -path '*/debug/*' | sort)
|
||||
while IFS= read -r f; do all_libs+=( "$f" ); done < <(find "$vcpkg_target_lib_dir" -name '*.a' -not -name 'libvoicecat*' | sort)
|
||||
libtool -static -o "$fat_lib" "${all_libs[@]}" 2>&1 | grep -v 'has no symbols' || true
|
||||
echo "[build-xcframework] $slice_name -> $fat_lib ($(stat -f%z "$fat_lib") bytes, fat)"
|
||||
}
|
||||
|
||||
args=()
|
||||
if $BUILD_MACOS; then build_slice apple-dev "macOS (arm64-osx)"; args+=( -library "$REPO_ROOT/build/apple-dev/lib/libvoicecat-fat.a" -headers "$APPLE_DIR/.staged-headers/macos" ); fi
|
||||
if $BUILD_IOS_DEVICE; then build_slice apple-ios "iOS device (arm64-ios)"; args+=( -library "$REPO_ROOT/build/apple-ios/lib/libvoicecat-fat.a" -headers "$APPLE_DIR/.staged-headers/ios" ); fi
|
||||
if $BUILD_IOS_SIM; then build_slice apple-ios-sim "iOS sim (arm64-ios-sim)"; args+=( -library "$REPO_ROOT/build/apple-ios-sim/lib/libvoicecat-fat.a" -headers "$APPLE_DIR/.staged-headers/ios-sim" ); fi
|
||||
if $BUILD_MACOS; then build_slice apple-dev "macOS (arm64-osx)" "arm64-osx"; args+=( -library "$REPO_ROOT/build/apple-dev/lib/libvoicecat-fat.a" -headers "$APPLE_DIR/.staged-headers/macos" ); fi
|
||||
if $BUILD_IOS_DEVICE; then build_slice apple-ios "iOS device (arm64-ios)" "arm64-ios"; args+=( -library "$REPO_ROOT/build/apple-ios/lib/libvoicecat-fat.a" -headers "$APPLE_DIR/.staged-headers/ios" ); fi
|
||||
if $BUILD_IOS_SIM; then build_slice apple-ios-sim "iOS sim (arm64-ios-sim)" "arm64-ios-simulator"; args+=( -library "$REPO_ROOT/build/apple-ios-sim/lib/libvoicecat-fat.a" -headers "$APPLE_DIR/.staged-headers/ios-sim" ); fi
|
||||
|
||||
# ── Stage headers + module map ───────────────────────────────────────────────────
|
||||
# Each slice gets its own headers dir (xcodebuild -create-xcframework requires a -headers
|
||||
|
||||
Reference in New Issue
Block a user