Files
voice-cat/clients/apple/dotnet/README.md
T

60 lines
5.9 KiB
Markdown
Raw Normal View History

2026-09-16 17:34:16 +02:00
# Managed Apple clients
2026-09-19 15:43:37 +02:00
`VoiceCat.Mac` and `VoiceCat.iOS` are the native AppKit and UIKit C# clients. They target the .NET 10 Apple workloads and reference the same `VoiceCat.Core` and `VoiceCat.Audio` assemblies used by Windows and the managed CLI. UIKit was selected over MAUI to retain direct AVAudioSession/AVAudioEngine lifecycle control and native VoiceOver behavior without another UI abstraction.
2026-09-16 17:34:16 +02:00
The managed client now implements the Swift client's functional surface: profiles and Keychain authentication, TOFU, protected channels, hierarchical channel presentation and roster state, channel and modeless private text, microphone/auxiliary/screen-audio streams, selectable Core Audio devices, VAD/PTT/always-on input, stereo microphone, RNNoise, per-stream receive tuning, self/server mute and deafen, full channel configuration, moderation, permissions, account administration, event sounds and speech. It imports the legacy Swift profile, TOFU and Keychain state during cutover. Capture is converted to interleaved 48 kHz int16 PCM through `AVAudioConverter`; playback converts the shared bounded `PcmRing` into Core Audio's native planar Float32 layout inside an allocation-free, non-blocking `AVAudioSourceNode` callback.
The managed clients are the supported implementation. Manual VoiceOver and real multi-human call validation remain release gates. Produce and validate an ad-hoc Release bundle with `zsh clients/apple/dotnet/publish-macos.sh --dry-run`. The script prefers `/usr/local/share/dotnet/dotnet`, where the pinned Apple workload is installed; set `VOICECAT_DOTNET` to override that host. Signing is performed on `bin/Release/distribution/VoiceCat.app`, leaving MSBuild's incremental app bundle untouched. For distribution, set `VOICECAT_CODESIGN_IDENTITY`; setting `APPLE_ID`, `APPLE_TEAM_ID`, and `APPLE_APP_PASSWORD` additionally submits, staples, and Gatekeeper-validates the notarized bundle.
2026-09-16 17:34:16 +02:00
2026-09-18 18:51:53 +02:00
Build on Apple Silicon macOS 27 with Xcode 27, .NET SDK 10.0.401 and workload set 10.0.401. Homebrew `protobuf` supplies a native arm64 `protoc`; the current `Grpc.Tools` package contains only an x64 macOS compiler.
2026-09-16 17:34:16 +02:00
```bash
2026-09-18 18:51:53 +02:00
sudo dotnet workload install macos ios --version 10.0.401
brew install protobuf # if /opt/homebrew/bin/protoc is not already present
cmake -S native/media -B dotnet/artifacts/native-build -DCMAKE_BUILD_TYPE=Release -DVOICECAT_DOTNET_RID=osx-arm64
2026-09-18 18:51:53 +02:00
cmake --build dotnet/artifacts/native-build --config Release --target voicecat_media --parallel 2
cmake --install dotnet/artifacts/native-build --config Release --component DotnetMedia --prefix dotnet/artifacts/native
2026-09-16 17:34:16 +02:00
dotnet restore clients/apple/dotnet/VoiceCat.Apple.slnx
dotnet build clients/apple/dotnet/VoiceCat.Apple.slnx -c Debug
2026-09-18 18:51:53 +02:00
open clients/apple/dotnet/VoiceCat.Mac/bin/Debug/net10.0-macos27.0/osx-arm64/VoiceCat.app
2026-09-16 17:34:16 +02:00
```
2026-09-19 15:43:37 +02:00
The iOS build first stages static device and simulator Opus/RNNoise archives, then builds the UIKit host. MSBuild also builds and embeds the existing Swift ReplayKit upload extension; that deliberately remains Swift because of the extension's tight memory budget and unsupported managed extension runtime.
```bash
./dotnet/build-native-ios.sh
dotnet build clients/apple/dotnet/VoiceCat.iOS/VoiceCat.iOS.csproj -c Debug -r iossimulator-arm64
```
2026-09-19 19:33:10 +02:00
For a physical device, use the checked-in build and deployment wrappers. The iPhone must be
paired and trusted, and Xcode must have an Apple Development identity and provisioning profile
for both `me.iamtalon.voicecat` and `me.iamtalon.voicecat.broadcast`. Let automatic signing
select them, or set `VOICECAT_CODESIGN_KEY`, `VOICECAT_CODESIGN_PROVISION`, and
`VOICECAT_DEVELOPMENT_TEAM` before building. The development team is required by the retained
ReplayKit extension's Xcode build. Set `VOICECAT_ALLOW_PROVISIONING_UPDATES=1` only when Xcode
needs to create or download a profile.
2026-09-19 19:33:10 +02:00
```bash
clients/apple/dotnet/build-ios-device.sh --configuration Debug
clients/apple/dotnet/deploy-ios-device.sh --list
clients/apple/dotnet/deploy-ios-device.sh --device "My iPhone" --configuration Debug --console
```
The build stages the verified app at `dist/ios-managed-device/VoiceCat.iOS.app`. Pass
`--no-build` to the deployment script for quick reinstall cycles. On iOS 1826, screen audio
uses the retained ReplayKit extension. On iOS 27 and newer, the host uses a small dynamically
2026-09-19 20:09:00 +02:00
loaded ScreenCaptureKit bridge and writes the same versioned ring; the Settings row toggles
that capture on and off. This keeps one managed consumer and allows the app to remain launchable
on older supported systems.
While joined to voice, the active `PlayAndRecord` session and audio engine remain running when
the scene backgrounds or the device locks, which keeps microphone capture, peer playback and the
screen-audio pump eligible for the declared `audio` background mode. Foreground activation,
hardware route changes, audio interruptions and media-service resets revalidate or rebuild the
audio graph. Validate this behavior on hardware: iOS simulator lifecycle transitions do not prove
background execution, lock-screen routing or Bluetooth recovery.
2026-09-19 19:33:10 +02:00
2026-09-19 15:43:37 +02:00
Profiles, TOFU state, passwords and the ReplayKit ring use the signed App Group `group.me.iamtalon.voicecat`; the managed client migrates the old app-private profile files on first use. The shared ring ABI is frozen in [`docs/broadcast-ring-format.md`](../../../docs/broadcast-ring-format.md).
2026-09-18 18:51:53 +02:00
The native build stages an `osx-arm64` `libvoicecat_media.dylib`; that shim contains only Opus/RNNoise. Debug builds deliberately omit hardened runtime so an ad-hoc-signed local app can load the separately ad-hoc-signed .NET runtime libraries without an Apple Development identity. Release builds retain hardened runtime for Developer ID signing and notarization. Only a macOS host can link, launch, grant microphone access and verify live devices. Final audio quality is validated with real multi-human calls after the feature surface is complete; a synthetic ten-minute sine-wave listen is deliberately not a release gate.