Mutter for iOS
The SwiftUI client connects directly to Mumble servers and uses libopus, Apple audio processing, and WebRTC screen viewing and sharing. It also provides Live Activities, Dynamic Island, and App Intents.
Commands below run from the repository root. Shared fonts and themes live in design/.
Build and install
Requirements: Xcode 26.6, iOS 17+ device or simulator, XcodeGen.
brew install xcodegen
cd mutter
xcodegen generate
open Mutter.xcodeproj
Signing reads your Team ID from Local.xcconfig, which is gitignored so it stays out of this
public repo. Copy Local.xcconfig.example to Local.xcconfig, put your 10-character Team ID in
it, and re-run xcodegen generate. Setting the team in Xcode's Signing & Capabilities tab instead
works until the next regenerate, which throws it away.
Screen sharing uses a Broadcast Upload Extension (MutterBroadcast, bundle id
com.alaarab.mutter.broadcast) and the App Group group.com.alaarab.mutter, shared with the app.
With automatic signing, Xcode registers both the first time it provisions them. If it can't, create
the App Group in the Apple Developer portal and enable it for com.alaarab.mutter and
com.alaarab.mutter.broadcast, then build again.
Then build the Mutter scheme. The app, its widget and the broadcast extension share one version:
change MARKETING_VERSION and CURRENT_PROJECT_VERSION in project.yml.
For repeatable phone deployment, copy Local.deploy.json.example to the ignored
Local.deploy.json. Set the paired device identifier from xcrun devicectl list devices
and the paths to this Mac's existing build keychain and its owner-only password file.
Then run:
python3 scripts/deploy-phone.py
The command unlocks the build keychain, puts it first while signing, and restores the
previous keychain search order afterward. This avoids selecting duplicate signing keys
from a locked login keychain. It then verifies the signature, installs the update, and
launches Mutter. --device overrides the saved phone; optional derived_data and
source_packages paths let you reuse an existing build and Swift package cache.
Omit the keychain fields when using Xcode's ordinary interactive signing setup.
TestFlight
TestFlight builds need an app record for com.alaarab.mutter in App Store Connect, and a Mac
that can sign for your team. Put an App Store Connect API key in ~/.config/ios-release.json
(key_id, issuer_id, key_path pointing at the .p8 file) for export and upload. Archive
signing and App Group provisioning use the Apple account signed into Xcode. If Local.deploy.json names a
build keychain, the release uses it the same way the phone deploy does. Then run:
python3 scripts/release-ios.py --build-number 2 # signed archive and IPA
python3 scripts/release-ios.py --build-number 3 --upload # upload to TestFlight
Use a build number higher than any already uploaded. The version comes from MARKETING_VERSION
in project.yml. Archives go under ~/Library/Developer/Xcode/Archives/mutter/. Uploading only
sends the build to TestFlight; it does not submit it for App Review.
Git authentication is configured per checkout. If the Mac's default GitHub host uses
another repository's deploy key, use an SSH URL and select the appropriate account key
with local core.sshCommand. Keep machine paths and credentials out of committed files.
Build notes:
- Opus headers.
Mutter/Audio/OpusShim/opus_shim.hincludes<opus/opus.h>with fallbacks. The shim exists becauseopus_encoder_ctlis variadic and Swift cannot call it. Opus-iOS 1.9.0 retains quoted includes and an incomplete framework umbrella. The app imports binary package frameworks through a system framework search path so their headers are treated as vendor code, including on a fresh Clang module build. Mutter's own warnings stay enabled. - Swift concurrency warnings. The project currently uses Swift 5 language mode with minimal checking; moving to Swift 6 requires a separate concurrency pass.
- Keychain entitlement. Certificates live in the keychain; the entitlements file is generated
by XcodeGen from
project.yml. - App Intents metadata. Extraction stays enabled for the app and widgets and is skipped for the broadcast extension and test targets, which define no intents. The scheme uses project-owned unit test bundles with the same Swift package test sources so this setting also applies to protocol and client tests. Compiler warnings stay enabled.
Tests
swift test --package-path Packages/MumbleCoreruns the protocol and client package tests on macOS.- The scheme's Test action runs those plus the UI tests. The chat keyboard tests connect to the
fake server, so start
node web/test/fake-server.mjs 64740on the Mac first; without it they skip. node --test web/test/native.test.mjs(macOS) compiles the app's audio playout, credential storage, reconnect, screen-signaling and ICE batching code into small probes. Run it after changing those files, includingMutter/ScreenShare/ICECandidateBatcher.swift.
Source layout
Mutter/: app state, SwiftUI screens, audio, and platform integration.Packages/MumbleCore/: portable protocol codecs and Apple network transport.MutterWidgets/: Live Activity and Dynamic Island extension.MutterUITests/: appearance and interaction regression tests.
The Xcode project is generated from project.yml. Keep signing and deployment settings in the
ignored local configuration files. See feature coverage for platform differences.