From 76c5d50658f798fcf852ccf33dac6fe550a74e7b Mon Sep 17 00:00:00 2001 From: coherent-cache Date: Sat, 18 Apr 2026 03:35:55 +0530 Subject: [PATCH 1/2] Key device ForEach by AudioDevice instead of AudioObjectID A CoreAudio device with both input and output streams (virtual loopback drivers like ZoomAudioDevice, Microsoft Teams Audio, BlackHole 16ch, or aggregates) is created twice by AudioDeviceService.getDevices() - once per scope - and both AudioDevice values share the same AudioObjectID. Disconnected placeholders built via AudioDevice.disconnected(...) all use id = 0. With ForEach(..., id: \.id) SwiftUI emits "ID occurs multiple times" and collapses the list to undefined output. Keying by the full AudioDevice disambiguates input vs output and distinct placeholders. --- AudioPriorityBar/Views/DeviceListView.swift | 2 +- AudioPriorityBar/Views/MenuBarView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AudioPriorityBar/Views/DeviceListView.swift b/AudioPriorityBar/Views/DeviceListView.swift index 4077372..f9a1d05 100644 --- a/AudioPriorityBar/Views/DeviceListView.swift +++ b/AudioPriorityBar/Views/DeviceListView.swift @@ -21,7 +21,7 @@ struct DeviceListView: View { var body: some View { VStack(spacing: 4) { - ForEach(Array(devices.enumerated()), id: \.element.id) { index, device in + ForEach(Array(devices.enumerated()), id: \.element) { index, device in DraggableDeviceRow( device: device, index: index, diff --git a/AudioPriorityBar/Views/MenuBarView.swift b/AudioPriorityBar/Views/MenuBarView.swift index 39b5354..2b94933 100644 --- a/AudioPriorityBar/Views/MenuBarView.swift +++ b/AudioPriorityBar/Views/MenuBarView.swift @@ -373,7 +373,7 @@ struct HiddenDevicesToggleView: View { .buttonStyle(.plain) .popover(isPresented: $isExpanded, arrowEdge: .bottom) { VStack(alignment: .leading, spacing: 4) { - ForEach(allHiddenDevices, id: \.id) { device in + ForEach(allHiddenDevices, id: \.self) { device in HiddenDeviceRow(device: device) } } From 1d7589855af46e5042c520481f41d0106ddc3b9d Mon Sep 17 00:00:00 2001 From: coherent-cache Date: Sat, 18 Apr 2026 03:35:55 +0530 Subject: [PATCH 2/2] Clear both hidden lists when unhiding an output device unhideDevice(_:) previously consulted hiddenKey(for:) (which picks a list based on the device's current category) and cleared a single list. But two existing paths put an output device into a list that does not match its current category: - hideDeviceEntirely(_:) explicitly adds the device to both hiddenSpeakers and hiddenHeadphones. - "Ignore as headphone" / "Ignore as speaker" can hide a device in the non-current category. Clicking the eye in the ignored popover then appears to do nothing - the device is removed from one list but remains hidden in the other. Clear hiddenMics for inputs and both output lists for outputs so "stop ignoring" always succeeds. --- .../Services/PriorityManager.swift | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/AudioPriorityBar/Services/PriorityManager.swift b/AudioPriorityBar/Services/PriorityManager.swift index 337dc2f..aa67983 100644 --- a/AudioPriorityBar/Services/PriorityManager.swift +++ b/AudioPriorityBar/Services/PriorityManager.swift @@ -179,10 +179,21 @@ class PriorityManager { } func unhideDevice(_ device: AudioDevice) { - let key = hiddenKey(for: device) - var hidden = defaults.array(forKey: key) as? [String] ?? [] - hidden.removeAll { $0 == device.uid } - defaults.set(hidden, forKey: key) + // For outputs, remove from BOTH hidden category lists so the device + // becomes visible regardless of which mode the user is currently in. + // Otherwise clicking "unhide" in the ignored popover can silently + // land the device in a category whose section is not currently shown. + let keys: [String] + if device.type == .input { + keys = [hiddenMicsKey] + } else { + keys = [hiddenSpeakersKey, hiddenHeadphonesKey] + } + for key in keys { + var hidden = defaults.array(forKey: key) as? [String] ?? [] + hidden.removeAll { $0 == device.uid } + defaults.set(hidden, forKey: key) + } } func unhideDevice(_ device: AudioDevice, fromCategory category: OutputCategory) {