--- source: ~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/macos-window-management.mdc confidence: 0.9 namespace: work last_synced: 2026-04-28 alwaysApply: false --- # macOS Window Management and AppKit Patterns ## WindowsManager for Window Operations ALWAYS use WindowsManager for creating and managing browser windows: ```swift // ✅ CORRECT - WindowsManager usage @MainActor final class FeatureCoordinator { func openNewWindow() { let tabCollection = TabCollectionViewModel() WindowsManager.openNewWindow( with: tabCollection, burnerMode: .regular, droppingPoint: nil ) } func openWindowWithURL(_ url: URL) { let tabCollection = TabCollectionViewModel() let window = WindowsManager.openNewWindow(with: tabCollection) window?.tabCollectionViewModel.addTab(with: url) } } // ❌ INCORRECT - Direct window creation final class FeatureCoordinator { func openNewWindow() { let window = NSWindow() // Don't create windows directly window.makeKeyAndOrderFront(nil) } } ``` ## Window Controller Architecture Use NSWindowController for complex window management: ```swift // ✅ CORRECT - NSWindowController pattern final class FeatureWindowController: NSWindowController { private let viewModel: FeatureViewModel init(viewModel: FeatureViewModel) { self.viewModel = viewModel super.init(window: nil) setupWindow() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupWindow() { let contentViewController = FeatureViewController(viewModel: viewModel) window = NSWindow(contentViewController: contentViewController) window?.setContentSize(NSSize(width: 800, height: 600)) window?.minSize = NSSize(width: 400, height: 300) window?.center() window?.title = "Feature Window" // Configure window behavior window?.isRestorable = true window?.identifier = NSUserInterfaceItemIdentifier("FeatureWindow") } override func windowDidLoad() { super.windowDidLoad() // Additional window setup window?.delegate = self setupToolbar() } } // MARK: - NSWindowDelegate extension FeatureWindowController: NSWindowDelegate { func windowWillClose(_ notification: Notification) { // Clean up resources viewModel.cleanup() } func windowDidBecomeMain(_ notification: Notification) { // Handle window becoming main viewModel.windowDidBecomeActive() } } ``` ## Multi-Window State Management Use TabCollectionViewModel for window-specific state: ```swift // ✅ CORRECT - Window-specific state management @MainActor final class WindowCoordinator { private let tabCollectionViewModel: TabCollectionViewModel private weak var windowController: NSWindowController? init(tabCollectionViewModel: TabCollectionViewModel) { self.tabCollectionViewModel = tabCollectionViewModel } func currentTab() -> Tab? { return tabCollectionViewModel.selectedTab } func addNewTab(with url: URL? = nil) { tabCollectionViewModel.addTab(with: url) } func closeCurrentTab() { guard let currentTab = tabCollectionViewModel.selectedTab else { return } tabCollectionViewModel.removeTab(currentTab) } func closeWindow() { windowController?.close() } } ``` ## Window State Restoration Implement proper state restoration: ```swift // ✅ CORRECT - Window state restoration extension FeatureWindowController { override func restoreState(with coder: NSCoder) { super.restoreState(with: coder) // Restore window-specific state if let savedData = coder.decodeObject(forKey: "viewModelState") as? Data { viewModel.restoreState(from: savedData) } } override func encodeRestorableState(with coder: NSCoder) { super.encodeRestorableState(with: coder) // Save window-specific state if let stateData = viewModel.encodeState() { coder.encode(stateData, forKey: "viewModelState") } } } ``` ## NSViewController and SwiftUI Integration Use NSHostingView for SwiftUI integration: ```swift // ✅ CORRECT - NSHostingView integration final class FeatureViewController: NSViewController { private let viewModel: FeatureViewModel init(viewModel: FeatureViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadView() { view = NSHostingView(rootView: FeatureView(viewModel: viewModel)) } override func viewDidLoad() { super.viewDidLoad() title = "Feature" preferredContentSize = NSSize(width: 400, height: 300) } override func viewWillAppear() { super.viewWillAppear() viewModel.viewWillAppear() } override func viewDidAppear() { super.viewDidAppear() viewModel.viewDidAppear() } } ``` ## View Controller Lifecycle Follow AppKit view controller patterns: ```swift // ✅ CORRECT - AppKit lifecycle management final class FeatureViewController: NSViewController { private var observations: Set = [] override func viewDidLoad() { super.viewDidLoad() setupUI() bindViewModel() setupNotifications() } override func viewWillAppear() { super.viewWillAppear() viewModel.refreshData() } override func viewDidAppear() { super.viewDidAppear() // View is fully visible viewModel.trackViewAppearance() } override func viewWillDisappear() { super.viewWillDisappear() viewModel.saveUserChanges() } private func setupNotifications() { let observation = NotificationCenter.default.addObserver( forName: .dataUpdated, object: nil, queue: .main ) { [weak self] _ in self?.viewModel.refreshData() } observations.insert(observation) } deinit { observations.forEach { NotificationCenter.default.removeObserver($0) } observations.removeAll() } } ``` ## Memory Management for Multiple Windows Implement proper cleanup for window controllers: ```swift // ✅ CORRECT - Window memory management final class FeatureWindowController: NSWindowController { private var observers: Set = [] deinit { // Clean up resources observers.forEach { NotificationCenter.default.removeObserver($0) } observers.removeAll() viewModel.cleanup() } override func close() { // Prepare for closure viewModel.saveState() super.close() } func cleanupBeforeClose() { // Cancel any ongoing operations viewModel.cancelOngoingOperations() // Remove from window tracking WindowTracker.shared.removeWindow(self) } } ``` ## Window Cascading and Positioning Handle window positioning properly: ```swift // ✅ CORRECT - Window positioning extension WindowsManager { static func positionNewWindow(_ window: NSWindow) { if let lastWindow = NSApp.orderedWindows.first { let origin = lastWindow.frame.origin let offset: CGFloat = 30 let newOrigin = NSPoint( x: origin.x + offset, y: origin.y - offset ) // Ensure window stays on screen let screenFrame = window.screen?.visibleFrame ?? NSScreen.main?.visibleFrame ?? .zero if screenFrame.contains(NSRect(origin: newOrigin, size: window.frame.size)) { window.setFrameOrigin(newOrigin) } else { window.center() } } else { window.center() } } } ``` See `macos-system-integration.md` for system-level integration patterns and `macos-preferences.md` for preferences management.