docs+scripts: add iOS build and simulator run commands

scripts/build-ios-client.sh: builds VoiceCatiOS.app for iOS simulator
  - calls build-xcframework.sh --all (all 3 slices required for iOS sim slice)
  - xcodebuild -target VoiceCatiOS with SYMROOT=OBJROOT to co-locate SPM
    and app build products (fixes "unable to resolve module dependency" error)
  - stages result to dist/ios-client/

scripts/run-ios-simulator.sh: installs and launches on an iPhone simulator
  - finds a booted simulator or boots the first available iPhone sim
  - opens Simulator.app, installs via simctl install, launches via simctl launch
  - --build flag to build first; --log to stream app logs; --device / --udid to
    pin a specific simulator

scripts/build-all.sh: add --ios-client opt-in flag (macOS only)

docs/building.md:
  - add iOS entry to quick-nav table
  - update §6 (apple platform): remove "scaffolding" caveat now that iOS slices
    are validated; trim to essentials + pointer to build-xcframework.sh
  - add §9 (iOS client SwiftUI): XCFramework, xcodebuild command with rationale
    for -target/-SYMROOT flags, run script usage, full simctl commands, Xcode UI
This commit is contained in:
2026-06-19 02:19:38 +02:00
parent e26e7db5b1
commit bf37fe8f0f
4 changed files with 378 additions and 13 deletions

View File

@@ -14,7 +14,9 @@ and *how to drive the binaries by hand*.
| Server + `vccli` + tests (all platforms) | [§3](#3-build--test-the-loop-youll-run-constantly) | `cmake --preset dev && cmake --build --preset dev && ctest --preset dev` |
| Production server (stripped, no tests) | [§5](#5-server-release-production-shaped-build) | `cmake --preset server-release && cmake --build --preset server-release` |
| Windows client (C# / WinForms) | [§7](#7-windows-client-c--winforms) | `dotnet build clients/windows/VoiceCat.slnx` |
| macOS client (AppKit) | [§8](#8-macos-client-appkit) | `xcodebuild -project clients/apple/macOS/VoiceCatMac.xcodeproj -scheme VoiceCatMac build` |
| macOS client (AppKit) | [§8](#8-macos-client-appkit) | `scripts/build-macos-client.sh` |
| iOS client (SwiftUI / simulator) | [§9](#9-ios-client-swiftui) | `scripts/build-ios-client.sh` |
| Launch iOS app on simulator | [§9](#9-ios-client-swiftui) | `scripts/run-ios-simulator.sh` |
| Swift core + tests | [§8](#8-macos-client-appkit) | `cd clients/apple && swift test` |
## 1. What each preset is for
@@ -214,13 +216,11 @@ Use this to sanity-check release-mode behavior (e.g. perf, optimized codepaths)
day-to-day development, since it has no test target wired up. The `-s` linker flag strips
symbol tables from the binaries, producing smaller executables suitable for distribution.
## 6. Apple platform builds (scaffolding)
## 6. Apple platform builds
The `apple-dev`, `apple-ios`, and `apple-ios-sim` presets produce static `libvoicecat.a`
slices for the Swift Package / XCFramework. The `apple-dev` preset (macOS slice) is
**validated** — it builds green on macOS 26.5 / Apple Silicon and produces a valid arm64
`.a` + XCFramework. The iOS cross-compile presets (`apple-ios`, `apple-ios-sim`) are still
**scaffolding** — not yet CI-validated. Build on macOS:
slices for the Swift Package / XCFramework. All three are validated on macOS 26.5 / Apple
Silicon. Build on macOS:
```bash
# macOS slice (arm64-osx on Apple Silicon, x64-osx on Intel)
@@ -228,22 +228,31 @@ cmake --preset apple-dev
cmake --build --preset apple-dev
# → build/apple-dev/lib/libvoicecat.a
# iOS device slice
# iOS device slice (arm64-ios)
cmake --preset apple-ios
cmake --build --preset apple-ios
# → build/apple-ios/lib/libvoicecat.a
# iOS simulator slice
# iOS simulator slice (arm64-ios-simulator)
cmake --preset apple-ios-sim
cmake --build --preset apple-ios-sim
# → build/apple-ios-sim/lib/libvoicecat.a
```
The three `.a` files are then stitched into an XCFramework via `xcodebuild
-create-xcframework` (see [clients/apple/README.md](../clients/apple/README.md) for the
planned workflow). Actual XCFramework stitching, `AVAudioSession` integration, and iOS
UI work are tracked as follow-up tasks — the presets exist so the build entry point is ready
when that work starts.
You rarely need to run these CMake commands directly. The
`clients/apple/scripts/build-xcframework.sh` script drives them internally and stitches
the result into a self-contained `VoiceCatCore.xcframework`:
```bash
# macOS slice only (default — used by macOS AppKit client)
clients/apple/scripts/build-xcframework.sh
# All 3 slices (macOS + iOS device + iOS sim — required for iOS client)
clients/apple/scripts/build-xcframework.sh --all
```
See [clients/apple/README.md](../clients/apple/README.md) for the XCFramework internals
(fat static lib merge, module map staging).
## 7. Windows client (C# / WinForms)
@@ -347,3 +356,124 @@ open clients/apple/macOS/VoiceCatMac.xcodeproj
# Terminal 2 — launch the client
open ~/Library/Developer/Xcode/DerivedData/VoiceCatMac-*/Build/Products/Debug/VoiceCatMac.app
```
Or use the per-artifact script which builds and stages to `dist/macos-client/`:
```bash
scripts/build-macos-client.sh
```
## 9. iOS client (SwiftUI)
The iOS client is an Xcode project (`clients/apple/iOS/VoiceCatiOS.xcodeproj`) that
links `libvoicecat` via the same `VoiceCatCore` Swift Package as the macOS client.
Full details in [`clients/apple/README.md`](../clients/apple/README.md).
**Prerequisites:** Xcode, vcpkg (`VCPKG_ROOT` set), iOS Simulator runtime installed
(Xcode > Settings > Platforms > iOS). iOS deployment target: 17.0.
### Build the XCFramework (all slices)
The iOS build requires the `ios-arm64-simulator` slice in the XCFramework — not just the
macOS slice. Use the `--all` flag to produce all three slices:
```bash
clients/apple/scripts/build-xcframework.sh --all
# → clients/apple/VoiceCatCore.xcframework/
# ├── macos-arm64/
# ├── ios-arm64/
# └── ios-arm64-simulator/
```
### Build the iOS simulator app
```bash
# Via the convenience script (recommended):
scripts/build-ios-client.sh
# → clients/apple/iOS/build/Debug-iphonesimulator/VoiceCatiOS.app
# → dist/ios-client/VoiceCatiOS.app
# Or as a single command (what the script does under the hood):
SIM_SDK="iphonesimulator$(xcrun --sdk iphonesimulator --show-sdk-version)"
BUILD_DIR="$(pwd)/clients/apple/iOS/build"
xcodebuild \
-project clients/apple/iOS/VoiceCatiOS.xcodeproj \
-target VoiceCatiOS \
-sdk "$SIM_SDK" \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
ARCHS=arm64 \
ONLY_ACTIVE_ARCH=YES \
SYMROOT="$BUILD_DIR" \
OBJROOT="$BUILD_DIR" \
build
```
**Why `-target` instead of `-scheme -destination`?** Using `-scheme VoiceCatiOS
-destination 'platform=iOS Simulator,OS=latest'` requires a simulator runtime whose iOS
version exactly matches the SDK version (`iphonesimulatorX.Y`). If you have an older
runtime installed (common when the SDK ships ahead of runtime availability in Xcode), the
build fails with "Unable to find a destination matching the provided destination specifier."
Using `-target` bypasses destination matching and builds against the SDK directly.
**Why `SYMROOT=OBJROOT=clients/apple/iOS/build`?** When building with `-target` (not
`-scheme`), the local Swift Package (`VoiceCatCore`) resolves its build products relative
to `OBJROOT`. By default, SPM resolves into `clients/apple/build/`, while the app target
looks in `clients/apple/iOS/build/`. Pointing both to the same directory fixes the
"unable to resolve module dependency: 'VoiceCatCore'" error.
### Run on the iOS Simulator
```bash
# Via the script (finds or boots an iPhone simulator, installs, launches):
scripts/run-ios-simulator.sh
# Build and run in one step:
scripts/run-ios-simulator.sh --build
# Stream app logs after launch:
scripts/run-ios-simulator.sh --log
# Target a specific device by name or UDID:
scripts/run-ios-simulator.sh --device "iPhone 16 Pro"
scripts/run-ios-simulator.sh --udid XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
```
Under the hood the script uses `xcrun simctl` commands:
```bash
# Boot a simulator (if not already running):
xcrun simctl boot <UDID>
open -a Simulator
# Install the built .app:
xcrun simctl install <UDID> clients/apple/iOS/build/Debug-iphonesimulator/VoiceCatiOS.app
# Launch the app:
xcrun simctl launch <UDID> cat.voice.VoiceCatiOS
# Stream logs (Ctrl+C to stop — does not kill the app):
xcrun simctl spawn <UDID> log stream --predicate 'subsystem contains "VoiceCat"'
```
### Run the full stack (server + iOS simulator)
```bash
# Terminal 1 — start the server
./build/dev/bin/voicecat-server --name "My Server"
# Terminal 2 — build + launch iOS client on simulator
scripts/run-ios-simulator.sh --build
```
On first connect the app will show a TOFU identity sheet — accept it, then join a channel.
### Open in Xcode
```bash
open clients/apple/iOS/VoiceCatiOS.xcodeproj
```
Xcode can build and run on the simulator directly. The XCFramework must already exist
(`clients/apple/VoiceCatCore.xcframework/`) — run `build-xcframework.sh --all` once
before opening Xcode.