refactor: completely remove internal lastValidBattery dictionary for zero-cache live updates

This commit is contained in:
Nikita 2026-10-06 13:34:59 +05:00
parent ec302446d9
commit 5ad28b5bd9

View file

@ -17,7 +17,6 @@ public final class BluetoothTracker: ObservableObject {
@Published public var isConnected: Bool = false @Published public var isConnected: Bool = false
private var timerSource: DispatchSourceTimer? private var timerSource: DispatchSourceTimer?
private var lastValidBattery: [String: Int] = [:]
private typealias IntBatteryGetter = @convention(c) (AnyObject, Selector) -> Int private typealias IntBatteryGetter = @convention(c) (AnyObject, Selector) -> Int
private typealias BoolGetter = @convention(c) (AnyObject, Selector) -> Bool private typealias BoolGetter = @convention(c) (AnyObject, Selector) -> Bool
@ -35,7 +34,7 @@ public final class BluetoothTracker: ObservableObject {
stopTracking() stopTracking()
updateDeviceList() updateDeviceList()
// Kernel-level DispatchSourceTimer: Immune to RunLoop pauses or UI tracking modes // Kernel-level DispatchSourceTimer: strictly 1 second interval, no caching
let timer = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main) let timer = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue.main)
timer.schedule(deadline: .now() + 1.0, repeating: 1.0, leeway: .milliseconds(50)) timer.schedule(deadline: .now() + 1.0, repeating: 1.0, leeway: .milliseconds(50))
timer.setEventHandler { [weak self] in timer.setEventHandler { [weak self] in
@ -55,19 +54,24 @@ public final class BluetoothTracker: ObservableObject {
} }
private func setupBluetoothListeners() { private func setupBluetoothListeners() {
let notificationNames = [
"IOBluetoothDeviceWasConnectedNotification",
"IOBluetoothDeviceWasDisconnectedNotification",
"IOBluetoothDeviceNotification",
"IOBluetoothDeviceServicesResolvedNotification",
"IOBluetoothDeviceNameChangedNotification",
"IOBluetoothHandsFreeDeviceDidUpdateNotification"
]
for name in notificationNames {
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(handleBluetoothChange), selector: #selector(handleBluetoothChange),
name: NSNotification.Name("IOBluetoothDeviceWasConnectedNotification"), name: NSNotification.Name(name),
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleBluetoothChange),
name: NSNotification.Name("IOBluetoothDeviceWasDisconnectedNotification"),
object: nil object: nil
) )
} }
}
@objc private func handleBluetoothChange() { @objc private func handleBluetoothChange() {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
@ -84,11 +88,10 @@ public final class BluetoothTracker: ObservableObject {
var updatedList: [BluetoothDeviceModel] = [] var updatedList: [BluetoothDeviceModel] = []
let activeAudioName = getActiveCoreAudioDeviceName() let activeAudioName = getActiveCoreAudioDeviceName()
// 1. Scan IOBluetooth paired devices // 1. Scan IOBluetooth paired devices directly (zero caching)
if let pairedDevices = IOBluetoothDevice.pairedDevices() as? [IOBluetoothDevice] { if let pairedDevices = IOBluetoothDevice.pairedDevices() as? [IOBluetoothDevice] {
for pairedDev in pairedDevices { for pairedDev in pairedDevices {
let address = pairedDev.addressString ?? UUID().uuidString let address = pairedDev.addressString ?? UUID().uuidString
// Re-instantiate device to bypass any internal property caching
let device = IOBluetoothDevice(addressString: address) ?? pairedDev let device = IOBluetoothDevice(addressString: address) ?? pairedDev
let name = device.nameOrAddress ?? pairedDev.nameOrAddress ?? "Unknown Device" let name = device.nameOrAddress ?? pairedDev.nameOrAddress ?? "Unknown Device"
@ -130,7 +133,7 @@ public final class BluetoothTracker: ObservableObject {
} }
} }
// 2. IOKit Registry enrich // 2. IOKit Registry live enrich (zero caching)
enrichFromIORegistry(devices: &updatedList) enrichFromIORegistry(devices: &updatedList)
// 3. Sort: Connected audio first // 3. Sort: Connected audio first
@ -247,8 +250,6 @@ public final class BluetoothTracker: ObservableObject {
?? (fallbackDevice != nil ? getBatteryValue(from: fallbackDevice!, selectorName: selectorName) : nil) ?? (fallbackDevice != nil ? getBatteryValue(from: fallbackDevice!, selectorName: selectorName) : nil)
} }
let addr = model.address
if isMulti { if isMulti {
model.leftBattery = query(selectorName: "batteryPercentLeft") model.leftBattery = query(selectorName: "batteryPercentLeft")
model.rightBattery = query(selectorName: "batteryPercentRight") model.rightBattery = query(selectorName: "batteryPercentRight")
@ -262,12 +263,7 @@ public final class BluetoothTracker: ObservableObject {
?? query(selectorName: "batteryPercentCombined") ?? query(selectorName: "batteryPercentCombined")
?? query(selectorName: "batteryPercentLeft") ?? query(selectorName: "batteryPercentLeft")
if let single = single {
lastValidBattery[addr] = single
model.singleBattery = single model.singleBattery = single
} else if let cached = lastValidBattery[addr] {
model.singleBattery = cached
}
} }
} }