iOS P0 acceptance — human-must test cases (v1.0.0-rc.8)
This document captures the human-side verification pass for the
iOS P0 ship of Chanora. The officially-tested environment is
iOS 17 or later on a physical iPhone signed with the developer's
personal team (DEC-025). Simulator support is best-effort —
microphone capture is simulated by the host's mic but global PTT
behaviour cannot be exercised in the simulator.
Automated tests (cargo check, cargo test --workspace --lib)
sign off the mechanical side. This checklist signs off the user-
experience side. A failure on any row blocks promotion of the iOS
build from rc.8.
Source: this checklist mirrors the macOS, Linux, and Windows
acceptance documents. Auto-test sign-off lives in this document's
"Auto-test sign-off" section. SDD references: SDD-094..097, SDD-098..104 (v1
audio + PTT lifecycle), DEC-025 (target environment), DEC-027
(diagnostic privacy invariant), SRS-197 (iOS audio routing
contract), SRS-198 (honest capability advertising under runtime
permission state).
iOS does NOT have a global PTT capability comparable to Windows /
macOS / Linux desktops. The desktop PTT backend ladder
(SDD-081..088) collapses to FocusedPttBackend on iOS and the
capability badge always advertises L0Focused. PTT works only
while Chanora itself is the foreground app. This is intentional
per DEC-025 — Apple's iOS sandbox model has no path to a
session-level event tap analogue.
0. Pre-flight
| # |
Step |
Expected |
| 0.1 |
Open apps/chanora_flutter/ios/Runner.xcworkspace in Xcode once. In Runner target → Signing & Capabilities → set your personal team (free Apple ID team is sufficient for the P0 device pass). Set a unique bundle identifier (e.g. your.name.chanoraFlutter) — the default com.example.chanoraFlutter may already be claimed in the App Store registry. |
Xcode shows "Provisioning Profile: Xcode Managed Profile" with no errors. |
| 0.2 |
Connect a physical iPhone via USB and trust the development Mac. On the iPhone: Settings → General → VPN & Device Management → developer-cert → Trust. |
iPhone appears in Xcode's device picker. |
| 0.3 |
Build the bridge for the device: cd ~/chanora && rustup target add aarch64-apple-ios && cargo build --release --target aarch64-apple-ios -p chanora_bridge. |
target/aarch64-apple-ios/release/libchanora_bridge.a is produced. |
| 0.4 |
Build the Flutter app: cd apps/chanora_flutter && flutter build ios --release. |
build/ios/iphoneos/Runner.app exists. |
| 0.5 |
Install + launch on the iPhone: in Xcode pick the device + Runner scheme → Run. |
App launches on phone. |
| 0.6 |
On first launch the iPhone prompts for Microphone access — Allow. |
Prompt text matches NSMicrophoneUsageDescription from ios/Runner/Info.plist. |
| 0.7 |
About / version label inside Chanora |
Shows v1.0.0-rc.8. |
TC-1: Connect + identity persistence (DEC-006, SRS-201)
| # |
Step |
Expected |
| 1.1 |
Enter a reachable TS3 server address, a nickname, blank password |
Connect button enabled |
| 1.2 |
Tap Connect |
Server snapshot appears |
| 1.3 |
Quit (background) + relaunch with the same nickname |
Server-visible UID is the same (identity persisted in the app-private support directory as identity.tskey) |
TC-2: Voice channel join (SRS-204, SDD-094)
| # |
Step |
Expected |
| 2.1 |
Tap a voice channel |
Join works; voice bar shows the channel name. Mic capture starts (the iOS status bar may show a red microphone indicator). |
| 2.2 |
Tap a different voice channel |
Smooth move; no audio dropout. |
TC-3: PTT press / release (Focused only — iOS limitation, SDD-103)
| # |
Step |
Expected |
| 3.1 |
Open Voice settings (gear icon), select Push to talk |
The capability badge clearly states L0 Focused with explanatory text noting iOS does not permit global hotkeys. |
| 3.2 |
Bind a key — the only "bound" key on iOS is the on-screen PTT button in the Voice Bar |
"Bound key" UI is replaced by an on-screen Push-to-Talk button. |
| 3.3 |
Touch and hold the PTT button |
Mic indicator turns "Mic on" while held; releases on touch-up. |
TC-4: Release tail (SDD-096)
| # |
Step |
Expected |
| 4.1 |
Set release-tail to 0 ms; tap-and-release the PTT button |
Sharp on/off transition. |
| 4.2 |
Set release-tail to 500 ms; tap-and-release |
Mic stays on for ~500 ms after release. |
TC-5: Continuous mode
| # |
Step |
Expected |
| 5.1 |
Switch to Continuous mode |
PTT button disappears; mic stays open while in the voice channel. |
TC-6: Mic mute
| # |
Step |
Expected |
| 6.1 |
Mute mic via the Voice Bar |
Mic icon shows muted state; touching the PTT button does not unmute it. |
TC-7: Speaker mute
| # |
Step |
Expected |
| 7.1 |
Speaker mute |
Other clients silenced locally. |
TC-8: Background audio (UIBackgroundModes = audio, SDD-102)
| # |
Step |
Expected |
| 8.1 |
While connected + in a voice channel + in Continuous mode, swipe Chanora to the background. |
The iOS red microphone indicator stays on; voice continues to be received and sent. |
| 8.2 |
Return to Chanora |
Session intact; no reconnect cycle. |
| 8.3 |
Lock the iPhone for 30 s, then unlock |
Session still intact (provided you stayed in the channel and on cellular/wifi network). |
TC-9: AVAudioSession routing (SDD-098, SDD-100, SDD-101)
| # |
Step |
Expected |
| 9.1 |
With voice connected, place an iPhone phone call to yourself (or have someone call). |
Chanora's audio session yields to the phone call (iOS owns the audio focus). |
| 9.2 |
End the phone call |
Chanora resumes audio without manual reconnect. |
| 9.3 |
Connect AirPods / Bluetooth headset; talk on Chanora |
Audio routes to the headset (.allowBluetoothHFP + .allowBluetoothA2DP are set in AppDelegate.swift). |
TC-10: Reconnect
| # |
Step |
Expected |
| 10.1 |
Toggle Airplane Mode on for 10 s, then off |
Connection-lost banner appears, then recovers. |
TC-11: Diagnostic export + privacy (DEC-027)
| # |
Step |
Expected |
| 11.1 |
Export a diagnostic bundle |
Allow-listed fields only; no raw key labels, no audio bytes. |
TC-12: Capability badge labels (SDD-103)
| # |
Step |
Expected |
| 12.1 |
Capability badge |
Shows L0Focused with explanation that iOS does not expose global hotkeys. |
Auto-test sign-off
These rows do not require human interaction.
| Layer |
Result |
cargo check --workspace on macOS host |
clean |
cargo test --workspace --lib on macOS host |
unchanged (iOS-specific Rust code is #[cfg(target_os = "ios")]-gated and not exercised in host tests) |
cargo build --release --target aarch64-apple-ios -p chanora_bridge |
clean — produces libchanora_bridge.a |
flutter build ios --release |
clean — produces Runner.app |
AppDelegate.swift registers AVAudioSession.routeChangeNotification and AVAudioSession.interruptionNotification observers (SDD-100, SDD-101) |
Log lines chanora_flutter: route change reason=… and chanora_flutter: audio interruption began/ended in device console. |
Sign-off form
Known gaps (not blockers)
- TestFlight + App Store distribution: requires the paid $99/yr Apple Developer Program (the personal team is sufficient for sideloaded device testing but not for TestFlight). P1 follow-up.
- Simulator audio: cpal-via-CoreAudio inside the iOS simulator routes to the Mac's audio devices and is fine for smoke-testing the UI, but real microphone capture / AEC behaviour cannot be assessed there. Test on a physical iPhone.
- VAD (
TransmitMode::VoiceActivity) is intentionally disabled in P0 (DEC-030).
- iPad layout: this checklist targets iPhone. The wide-mode (≥ 840 dp) UI works on iPad but is not signed off here.