Files
TopManager/TopManager/TopManagerApp.swift
hariel1985 186ae31d01 Fix: show alert banners while TopManager is foreground
macOS suppresses an app's own notifications while it is the frontmost app unless
the UNUserNotificationCenter delegate opts in. Set the delegate in AppDelegate and
return [.banner, .sound, .list] from willPresent so High-CPU/health alerts pop as
banners even with the window focused. (Also confirmed notifications require a real
code signature, not the ad-hoc CODE_SIGNING_ALLOWED=NO test build.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 21:00:57 +02:00

70 sor
2.4 KiB
Swift

import SwiftUI
import UserNotifications
/// Flushes persisted metrics history when the app is quit, and lets alert banners
/// appear even while TopManager is the foreground app (macOS suppresses an app's
/// own notifications in the foreground unless the delegate opts in).
final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
UNUserNotificationCenter.current().delegate = self
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .sound, .list])
}
func applicationWillTerminate(_ notification: Notification) {
MetricsStore.shared.flush()
}
}
@main
struct TopManagerApp: App {
@StateObject private var monitor = SystemMonitor.shared
@StateObject private var settings = AppSettings.shared
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(monitor)
.environmentObject(AlertCenter.shared)
.environmentObject(settings)
.onAppear {
monitor.startMonitoring()
settings.applyAll()
AlertCenter.shared.requestNotificationAuthorization()
}
}
.windowStyle(.automatic)
.defaultSize(width: 1000, height: 700)
.onChange(of: scenePhase) { phase in
if phase == .background {
MetricsStore.shared.flush()
}
}
.commands {
CommandGroup(replacing: .newItem) { }
}
Settings {
SettingsView()
.environmentObject(settings)
}
// Menu Bar Extra with configurable metric
MenuBarExtra {
MenuBarView()
.environmentObject(monitor)
.environmentObject(AlertCenter.shared)
.environmentObject(settings)
} label: {
MenuBarLabel(monitor: monitor, settings: settings, alerts: AlertCenter.shared)
}
.menuBarExtraStyle(.window)
}
}