Files
obsidian-vault/work/wiki/apple-browsers/macos-window-management.md
T

308 lines
8.2 KiB
Markdown

---
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<NSObjectProtocol> = []
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<NSObjectProtocol> = []
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.