# Voice — iOS client for Hope Voice Native SwiftUI iOS app (iOS 17+) that talks to the [Hope Voice](https://github.com/) agent: voice/text conversation, transcript history, voice & model pickers, and a background-tasks panel. Uses `hope-voice-api`. > **Status:** Core functionality is implemented and verified in simulator to the extent the > environment allows. Full device qualification (mic capture, interruption behavior) > remains pending. The native Talk/Activity redesign is implemented — > see [PLAN.md](PLAN.md) and [docs/DEVICE_TESTING.md](docs/DEVICE_TESTING.md). ## Prerequisites - Xcode 16+ (Swift 6), iOS 17 simulator or a real iPhone on the LAN. - [xcodegen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`). - The `voice` container running on Unraid (LiveKit + token server). - Mic: the simulator mic is unreliable in this environment (CoreAudio `-4010`, 0 input channels) — voice mode needs a real device. If the simulator was wiped, grant permission first: `xcrun simctl privacy booted grant microphone dev.shane.voice`. ## Build & run ```sh xcodegen generate # regenerate Voice.xcodeproj after file changes xcodebuild -project Voice.xcodeproj -scheme Voice_iOS \ -destination 'generic/platform=iOS Simulator' build ``` The scheme is **`Voice_iOS`** (not `Voice`). Bundle id: `dev.shane.voice`. To run in the simulator, use Xcode or `xcrun simctl` with the built app. ## Server configuration Default base URL is `https://192.168.86.2:9178` (see `Sources/App/SettingsStore.swift`). Change it at runtime in **Settings → Server URL** (the app disconnects and recreates the session when idle). The app derives the LiveKit signaling endpoint from this base URL. ### TLS cert pinning The deployment uses a persistent self-signed cert. The entrypoint regenerates it if missing or issued for a different IP. The pinned SHA-256 in `Sources/Networking/TLSPinning.swift` must match after any actual cert rotation: ```sh openssl s_client -connect 192.168.86.2:9178 /dev/null \ | openssl x509 -outform der | openssl dgst -sha256 ``` Paste the new hash into `TLSPinning.pinnedCertSHA256Hex`. Until you do, token calls fail with a pin mismatch (not a network error). ## Features - **Conversation** — LiveKit room with the Hope agent; voice mode (mic + TTS) and text mode (composer), toggleable and persisted. State machine (idle/connecting/listening/thinking/ speaking) driven by audio levels, with a compact status indicator and mic meter. - **Transcripts** — partials updated in place, finals with timestamps, delivery-marker stripping, last-50 history persisted to UserDefaults, clear button. - **Voice & model pickers** — 13 Azure TTS voices; LLM models from the agent's `models` message. Both persist and sync on join (`set_voice`/`set_model`). - **Activity tab** — live `task_event` updates (steps, result, error) with an active count badge on the tab. ## Known limitations - **Simulator mic**: CoreAudio returns `-4010` (no usable input source) in this environment; voice capture must be verified on a real device. Text mode works in the simulator. - **Cert pin rotation**: see above — re-export the pin after each server cert regeneration. - **LiveKit signaling** uses plain `ws://192.168.86.2:7880` (not wss) because the Swift SDK cannot trust the self-signed cert on its WebSocket; token calls still go over pinned TLS. ## Docs - [docs/PROJECT_STATUS.md](docs/PROJECT_STATUS.md) — current handoff, decisions, and remaining work; read first. - [docs/VERIFICATION.md](docs/VERIFICATION.md) — test evidence and remaining qualification. - [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) — live NAS configuration and rollback. - [PLAN.md](PLAN.md) — phase-by-phase progress, verified facts, open items. - [docs/DEVICE_TESTING.md](docs/DEVICE_TESTING.md) — real-device qualification checklist. - [UPDATE_PATH.md](UPDATE_PATH.md) — original plan & protocol reference (read-only).