# Deploying the managed iOS app VoiceCat's supported iOS client is the .NET 10 UIKit application in `clients/apple/dotnet/VoiceCat.iOS`. The checked-in scripts build its native Opus/RNNoise dependency, compile and sign the managed app and Swift ReplayKit extension, stage the bundle, install it, and launch it on a paired physical device. ## Prerequisites - macOS 27, Xcode 27, .NET SDK 10.0.401, and the matching iOS workload. - An unlocked, trusted iPhone connected by USB or visible to Xcode over the network. - Apple Development profiles for `me.iamtalon.voicecat` and `me.iamtalon.voicecat.broadcast`, both with App Group `group.me.iamtalon.voicecat`. List available devices: ```bash clients/apple/dotnet/deploy-ios-device.sh --list ``` ## Build, install, and launch Set the Apple developer team explicitly. The .NET host and Xcode-built extension use separate build systems, and Xcode needs the team to select the extension profile. ```bash export VOICECAT_DEVELOPMENT_TEAM=FJV8L966W4 clients/apple/dotnet/build-ios-device.sh --configuration Debug clients/apple/dotnet/deploy-ios-device.sh \ --device "Talon’s iPhone" \ --configuration Debug \ --no-build ``` The verified bundle is staged at `dist/ios-managed-device/VoiceCat.iOS.app`. Omit `--no-build` to build and deploy in one command. Add `--console` to attach the launch to device logs. Confirm that the process stayed alive: ```bash xcrun devicectl device info processes --device "Talon’s iPhone" | rg VoiceCat ``` ## Signing findings The development team ID is `FJV8L966W4`. Do not infer it from the parenthesized suffix in an Apple Development certificate name; that value can identify the certificate holder and need not equal a profile's `TeamIdentifier`. The extension wrapper uses automatic signing with installed profiles. If a profile is absent and Xcode has a valid signed-in developer account, permit Xcode to create or download it: ```bash export VOICECAT_ALLOW_PROVISIONING_UPDATES=1 ``` Leave that variable unset when valid profiles are already installed. A stale command-line Xcode account can otherwise make provisioning updates fail even though offline signing can succeed. ## Troubleshooting ### Stale CMake source path After moving the native shim from `dotnet/native` to `native/media`, an old CMake cache can report a source-directory mismatch. Move the generated directories aside and rebuild: ```bash mv dotnet/artifacts/native-build-ios-arm64-cmake /tmp/ mv dotnet/artifacts/native-build-iossimulator-arm64-cmake /tmp/ ``` ### Locked restore reports changed runtime identifiers The managed libraries' lock files must include `ios-arm64` and `iossimulator-arm64`. Regenerate them after runtime changes, then verify that dependency versions did not change: ```bash /usr/local/share/dotnet/dotnet restore \ clients/apple/dotnet/VoiceCat.iOS/VoiceCat.iOS.csproj \ -p:VoiceCatIosStatic=true \ --force-evaluate ``` ### Developer disk image cannot be mounted CoreDevice errors 10003 or 12040 mean the phone locked. Unlock it, keep the display awake, and rerun deployment with `--no-build`. ## Verified hardware result On 2026-09-19, the Debug build completed with .NET 10.0.401 and Xcode 27.0. The host and ReplayKit extension were signed under team `FJV8L966W4`, installed wirelessly on Talon's iPhone, launched as `me.iamtalon.voicecat`, and remained in the device process list. Audio, background/lock behavior, Bluetooth, ReplayKit, ScreenCaptureKit, and VoiceOver remain separate manual hardware gates.