AppDelegate.swift
1import AppKit23class AppDelegate: NSObject, NSApplicationDelegate {4 // Optional backing storage rather than implicitly-unwrapped optionals:5 // before `applicationDidFinishLaunching` runs (or after `invalidate`),6 // there is genuinely no status item or menu controller to hand out, and7 // reading them through a normal optional forces the lifecycle through8 // a single funnel instead of letting any caller trap on a half-built9 // delegate.10 private var statusItem: NSStatusItem?11 private var menuController: MenuController?1213 func applicationDidFinishLaunching(_ notification: Notification) {14 // Create the status item in the menu bar15 let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)1617 if let button = item.button {18 // Use a system symbol for the icon19 button.image = NSImage(20 systemSymbolName: "photo.on.rectangle.angled", accessibilityDescription: "Haven")21 }2223 // Initialize the menu controller24 menuController = MenuController(statusItem: item)25 statusItem = item26 }2728 /// Read-only access to the status item created during launch. Returns29 /// `nil` when the delegate has not finished launching yet (or after30 /// `invalidate()` has been called). Callers that need the item to be31 /// present must consult this guard and fail gracefully otherwise.32 var currentStatusItem: NSStatusItem? { statusItem }3334 /// Read-only access to the menu controller created during launch.35 /// Returns `nil` before launch finishes so callers do not accidentally36 /// observe a half-built controller.37 var currentMenuController: MenuController? { menuController }3839 /// Tear down status-item and menu-controller state. Useful for tests40 /// that want to recreate the delegate cleanly and for any future code41 /// path that needs to dispose of the menu bar item explicitly.42 func invalidate() {43 statusItem = nil44 menuController = nil45 }46}