import AppKit import CoreLocation import CoreWLAN import Foundation struct WiFiLinkEvent: Identifiable { let id = UUID() let timestamp: Date let interfaceName: String let ssid: String? let rssi: Int let transmitRate: Double } @MainActor final class WiFiEventMonitor: NSObject, ObservableObject { @Published private(set) var isMonitoring = false @Published private(set) var interfaceDisplay = "Waiting for Wi-Fi" @Published private(set) var networkDisplay = "Current network unavailable" @Published private(set) var rssiDisplay = "--" @Published private(set) var transmitRateDisplay = "--" @Published private(set) var statusHeadline = "Ready to listen for link quality changes" @Published private(set) var statusMessage = "Click Start Monitoring to subscribe to `CWEventType.linkQualityDidChange` from CoreWLAN." @Published private(set) var locationMessage = "Grant access when prompted if you want the app to reveal the current Wi-Fi network name." @Published private(set) var monitorError: String? @Published private(set) var events: [WiFiLinkEvent] = [] private let wifiClient = CWWiFiClient.shared() private let locationManager = CLLocationManager() private var currentSSID: String? private var authorizationStatus: CLAuthorizationStatus = .notDetermined private var locationServicesEnabled = CLLocationManager.locationServicesEnabled() private var pendingMonitoringStart = false var requiresLocationAttention: Bool { currentSSID == nil && (showsLocationRequestButton || showsOpenSettingsButton) } var showsLocationRequestButton: Bool { locationServicesEnabled && authorizationStatus == .notDetermined } var showsOpenSettingsButton: Bool { !locationServicesEnabled || authorizationStatus == .denied || authorizationStatus == .restricted } override init() { super.init() wifiClient.delegate = self locationManager.delegate = self refreshAuthorization() refreshSnapshot() } func toggleMonitoring() { isMonitoring ? stopMonitoring() : startMonitoring() } func requestLocationAccess() { refreshAuthorization() guard locationServicesEnabled else { locationMessage = "Location Services is disabled system-wide. Turn it on in System Settings to reveal Wi-Fi identity details." return } if authorizationStatus == .notDetermined { pendingMonitoringStart = isMonitoring == false statusHeadline = "Waiting for location access" statusMessage = "macOS will show the permission prompt after this click." locationManager.requestWhenInUseAuthorization() } } func openLocationSettings() { let locations = [ URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_LocationServices"), URL(string: "x-apple.systempreferences:com.apple.Settings.PrivacySecurity.extension?Privacy_LocationServices"), URL(fileURLWithPath: "/System/Applications/System Settings.app"), ] for location in locations { guard let location else { continue } if NSWorkspace.shared.open(location) { break } } } private func startMonitoring() { monitorError = nil refreshAuthorization() refreshSnapshot() if showsLocationRequestButton { pendingMonitoringStart = true requestLocationAccess() return } doStartMonitoring() } private func doStartMonitoring() { guard wifiClient.interface() != nil else { isMonitoring = false statusHeadline = "No Wi-Fi interface available" statusMessage = "CoreWLAN did not return an active Wi-Fi interface on this Mac." monitorError = "No Wi-Fi interface was available to monitor." return } do { try wifiClient.startMonitoringEvent(with: .linkQualityDidChange) isMonitoring = true statusHeadline = "Monitoring live link-quality updates" statusMessage = "Watching for RSSI and transmit-rate changes from the active Wi-Fi interface." refreshSnapshot() } catch { isMonitoring = false monitorError = error.localizedDescription statusHeadline = "Unable to start monitoring" statusMessage = "The Wi-Fi monitor could not subscribe to the requested event." } } private func stopMonitoring() { do { try wifiClient.stopMonitoringAllEvents() isMonitoring = false pendingMonitoringStart = false statusHeadline = "Monitoring stopped" statusMessage = "The app is no longer subscribed to CoreWLAN events." monitorError = nil } catch { monitorError = error.localizedDescription } } private func refreshAuthorization() { locationServicesEnabled = CLLocationManager.locationServicesEnabled() authorizationStatus = locationManager.authorizationStatus if !locationServicesEnabled { locationMessage = "Location Services is turned off for this Mac, so macOS will hide the current Wi-Fi network identity." } else { switch authorizationStatus { case .authorizedAlways, .authorizedWhenInUse: locationMessage = "Location access is enabled. The app can show the current Wi-Fi network name when CoreWLAN provides it." case .denied: locationMessage = "Location access was denied. Link-quality events can still arrive, but the current network name will stay hidden." case .restricted: locationMessage = "Location access is restricted on this Mac. Link-quality events can still arrive without network identity details." case .notDetermined: locationMessage = "macOS may require location access before exposing the current Wi-Fi network name. Use the button above to request it." @unknown default: locationMessage = "The current location authorization state is unknown." } } } private func refreshSnapshot() { guard let interface = wifiClient.interface() else { interfaceDisplay = "No Wi-Fi Interface" networkDisplay = "Current network unavailable" rssiDisplay = "--" transmitRateDisplay = "--" return } interfaceDisplay = interface.interfaceName ?? "Unknown Interface" currentSSID = interface.ssid() if let currentSSID, !currentSSID.isEmpty { networkDisplay = currentSSID } else if locationServicesEnabled && authorizationStatus == .notDetermined { networkDisplay = "Current network unavailable" } else if locationServicesEnabled && (authorizationStatus == .denied || authorizationStatus == .restricted) { networkDisplay = "Grant location access to reveal SSID" } else { networkDisplay = "Current network unavailable" } let rssiValue = interface.rssiValue() rssiDisplay = rssiValue == 0 ? "--" : "\(rssiValue) dBm" let transmitRate = interface.transmitRate() transmitRateDisplay = transmitRate > 0 ? String(format: "%.0f Mbps", transmitRate) : "--" } private func appendEvent(interfaceName: String, rssi: Int, transmitRate: Double) { let ssid = wifiClient.interface(withName: interfaceName)?.ssid() let event = WiFiLinkEvent( timestamp: Date(), interfaceName: interfaceName, ssid: ssid, rssi: rssi, transmitRate: transmitRate ) events.insert(event, at: 0) if events.count > 24 { events.removeLast(events.count - 24) } } } extension WiFiEventMonitor: CLLocationManagerDelegate { nonisolated func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { Task { @MainActor [weak self] in guard let self else { return } self.refreshAuthorization() self.refreshSnapshot() guard self.pendingMonitoringStart, self.authorizationStatus != .notDetermined else { return } self.pendingMonitoringStart = false self.doStartMonitoring() } } } extension WiFiEventMonitor: CWEventDelegate { nonisolated func linkQualityDidChangeForWiFiInterface(withName interfaceName: String, rssi: Int, transmitRate: Double) { Task { @MainActor [weak self] in guard let self else { return } self.interfaceDisplay = interfaceName self.rssiDisplay = "\(rssi) dBm" self.transmitRateDisplay = String(format: "%.0f Mbps", transmitRate) self.appendEvent(interfaceName: interfaceName, rssi: rssi, transmitRate: transmitRate) self.refreshSnapshot() self.statusHeadline = "Last link-quality update received" self.statusMessage = "CoreWLAN reported a new RSSI or transmit-rate value at \(Self.timestampFormatter.string(from: Date()))." } } } private extension WiFiEventMonitor { static let timestampFormatter: DateFormatter = { let formatter = DateFormatter() formatter.timeStyle = .medium formatter.dateStyle = .none return formatter }() }