The simulator is crackling again: what changed with Device Hub and how to fix it
04 Nov 2026
If you run iOS simulators on a Mac, you may know this one: the speakers start crackling. Videos, music, a call, everything can pop while a simulator is booted. It's an old problem, and the Apple forums have threads about it going back to Xcode 12.
The usual fix is to give the simulator somewhere else to play. Install BlackHole 2ch, a virtual audio device that swallows everything you send to it, and pick it in Simulator.app under I/O → Audio Output. I did that once, years ago, and never thought about it again. Every new simulator played into BlackHole, and my speakers stayed clean.
Then Xcode 27 replaced Simulator.app with Device Hub, and the crackling came back.
What changed
Device Hub still has the setting. It's under Device → Sound → Sound Output, or in the Sound section of the Settings inspector. But it's now per simulator, and new simulators start on System, which means your Mac's speakers.
simctl shows it nicely. Every booted simulator I had looked like this:
$ xcrun simctl io <udid> enumerate
Class: Audio
Port Identifier: com.apple.CoreSimulator.Audio.HostRoute
Guest Audio Output -> Default Host Audio Device
Simulator.app kept one list for the whole app, RecentAudioOutputDeviceUIDs in the com.apple.iphonesimulator defaults, and mine still has BlackHole first. That's why I never had to pick it again. Device Hub doesn't read it. I also started a brand new simulator from Device Hub's own window, right after choosing BlackHole for another one, and it came up on System. Device Hub only read the setting. It never set it.
Watch what Device Hub does
So where does Device Hub put the choice? I took a snapshot of every relevant preferences domain, switched one simulator to BlackHole in the UI, and compared. Nothing. Device Hub didn't write a single preference.
The logs had the answer. With log stream running for Device Hub and CoreDevice, switching the output produced:
CoreDeviceService [SimulatorSetAudioOutputAction] Setting audio output device to: BlackHole2ch_UID
CoreDeviceService [SimulatorSetAudioOutputAction] Successfully set audio output device
It's a CoreDevice action, and CoreDevice has a command line tool. devicectl can do the same thing:
# read the current route
xcrun devicectl device info audio -d <udid>
# route the simulator to BlackHole
xcrun devicectl device settings audio -d <udid> --output-device BlackHole2ch_UID
The result ends up inside the simulator's own data, in data/var/run/com.apple.coresimulator.audio.plist:
sim_output_device_uid = BlackHole2ch_UID
useHostDefaultOutputDevice = false
How the setting behaves
Before building anything on top of it, I tested what keeps the setting and what loses it:
| Route | |
|---|---|
| Set while the simulator is shut down | Refused, it has to be booted |
| Reboot the simulator | Kept |
simctl clone | Copied to the clone |
| Erase the simulator | Lost, back to System |
| New simulator | System |
Fix 1: clone from a simulator that's already set
Since a clone copies the route, a simulator you clone from is a good place to set it once. My worktree simulators are clones of a base simulator, so I switched the two base simulators to BlackHole, and every new clone has been silent since.
That doesn't cover simulators you create from scratch, erase, or add in Device Hub.
Fix 2: a LaunchAgent that keeps System on BlackHole
For everything else, a small script runs every 30 seconds. It lists the booted simulators, reads each one's route file, and switches the ones on System to BlackHole:
#!/bin/zsh
# Keeps booted simulators that use the host default audio output routed to BlackHole.
# Device Hub stores the route per simulator and new or erased ones start on the
# default, so without this they play through the speakers. A simulator pinned to a
# specific device in Device Hub is left alone; choosing "System" there gets reverted.
OUTPUT_DEVICE=${SIM_AUDIO_OUTPUT_DEVICE:-BlackHole2ch_UID}
DEVICES_DIR=$HOME/Library/Developer/CoreSimulator/Devices
uses_host_default() {
local settings=$DEVICES_DIR/$1/data/var/run/com.apple.coresimulator.audio.plist
[[ ! -f $settings ]] && return 0
[[ $(plutil -extract useHostDefaultOutputDevice raw $settings 2>/dev/null) != false ]]
}
booted=$(xcrun simctl list devices booted | grep -oE '[0-9A-F]{8}(-[0-9A-F]{4}){3}-[0-9A-F]{12}')
for udid in ${(f)booted}; do
uses_host_default $udid || continue
xcrun devicectl device settings audio -d $udid --output-device $OUTPUT_DEVICE --quiet || continue
echo "$(date '+%F %T') $udid -> $OUTPUT_DEVICE"
done
Reading the file is instant, so devicectl only runs when something actually needs switching. A run with nothing to do takes about 0.15 seconds.
If you use a different virtual device, like BlackHole 16ch or Loopback, xcrun simctl io <udid> enumerate lists every host audio device with its UID under "Host Audio Devices". Put yours in OUTPUT_DEVICE.
Save it as ~/.local/bin/sim-audio-blackhole, make it executable, and add a LaunchAgent at ~/Library/LaunchAgents/com.example.sim-audio-blackhole.plist (replace YOU with your user name):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.sim-audio-blackhole</string>
<key>ProgramArguments</key>
<array>
<string>/Users/YOU/.local/bin/sim-audio-blackhole</string>
</array>
<key>StartInterval</key>
<integer>30</integer>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/YOU/.local/state/sim-audio-blackhole/log.txt</string>
<key>StandardErrorPath</key>
<string>/Users/YOU/.local/state/sim-audio-blackhole/log.txt</string>
</dict>
</plist>
Then load it:
chmod +x ~/.local/bin/sim-audio-blackhole
mkdir -p ~/.local/state/sim-audio-blackhole
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.sim-audio-blackhole.plist
To check it, I set a simulator to System in Device Hub. Fifteen seconds later it was back on BlackHole, and the log had a line for it.
To remove it, run launchctl bootout gui/$(id -u)/com.example.sim-audio-blackhole and delete the plist.
When you do want to hear a simulator
The script only touches simulators on System. If you want to listen to one, don't pick System in Device Hub, because the script will switch it back. Pick a specific device instead, like MacBook Pro Speakers or your AirPods. The script leaves that simulator alone until you set it back to System.
Still a workaround
None of this fixes the crackling itself. It only keeps the simulators away from your speakers, as a workaround for a bug that's been around at least since Xcode 12. Until simulators stop making the Mac's audio crackle, BlackHole keeps things quiet.
Sources
- Apple: Configuring the environment of a simulated device
- Apple Developer Forums: Simulator causing Mac audio distortion and How to Fix Cracking and Popping Sound?