Skip to main content
Nook follows a manager-based architecture where specialized managers handle distinct feature domains, coordinated through SwiftUI environment injection. This design separates concerns while maintaining type-safe communication between components.

Core architectural principles

Manager-based pattern

Each feature domain has a dedicated manager (TabManager, ProfileManager, etc.) that owns business logic and state.

Environment injection

Managers are injected via @EnvironmentObject and @Environment, eliminating global singletons (except legacy cases).

Reactive state

Models use Swift’s @Observable macro and Combine’s @Published for automatic UI updates.

MainActor confinement

All state management is @MainActor confined for thread safety.

Application entry flow

Nook’s initialization follows a hierarchical structure:
NookApp.swift (@main)           — App entry point, creates WindowGroup scene
  ├─ BrowserManager              — Central coordinator (being refactored)
  ├─ WindowRegistry              — Multi-window state tracking
  ├─ WebViewCoordinator          — WebView pool management
  └─ NookSettingsService         — User preferences

  ContentView.swift              — Per-window container, creates BrowserWindowState
  ├─ BrowserWindowState          — Window-specific state (current tab, sidebar width)
  └─ WindowRegistry.register()   — Registers window with global registry

  WindowView.swift               — Main browser UI layout
  ├─ Sidebar                     — Tab list, spaces, profiles
  ├─ WebsiteView                 — Active web content
  ├─ TopBar                      — URL bar, navigation controls
  └─ StatusBar                   — Download progress, status info

Environment injection flow

The dependency graph looks like this (from NookApp.swift:38-86):
// NookApp creates global coordinators
@State private var windowRegistry = WindowRegistry()
@State private var webViewCoordinator = WebViewCoordinator()
@State private var settingsManager = NookSettingsService()
@StateObject private var browserManager = BrowserManager()

// Injected into ContentView
ContentView()
  .environmentObject(browserManager)
  .environment(windowRegistry)
  .environment(webViewCoordinator)
  .environment(\.nookSettings, settingsManager)

// Each window gets its own state
@State private var defaultWindowState = BrowserWindowState()
WindowView()
  .environment(windowState)
BrowserManager is currently injected as @EnvironmentObject (legacy Combine pattern). Future refactoring will migrate it to @Environment with Swift Observation.

Window management

Nook supports multiple independent windows, each with its own state:
  • BrowserWindowState (Nook/Models/BrowserWindowState.swift:15) — Tracks current tab, space, profile, sidebar width, and UI flags per window
  • WindowRegistry (Nook/Managers/WindowRegistry/WindowRegistry.swift:14) — Single source of truth for all open windows
  • WebViewCoordinator (Nook/Managers/WebViewCoordinator/WebViewCoordinator.swift:14) — Manages webview instances across windows
When a user opens the same tab in multiple windows:
  1. First window gets the “primary” webview from Tab.webView
  2. Additional windows get “clone” webviews created by WebViewCoordinator.createCloneWebView() (WebViewCoordinator.swift:149)
  3. All webviews share the same WKWebViewConfiguration for extension/process pool consistency

Top-level modules

Nook’s codebase is organized into these directories:
DirectoryPurposeExample Files
App/Entry point, window management, app lifecycleNookApp.swift, ContentView.swift, AppDelegate.swift
Nook/Managers/~27 feature managers (business logic)BrowserManager/, TabManager/, ProfileManager/
Nook/Models/Data models and SwiftData entitiesTab/, Space/, Profile/, BrowserConfig/
Nook/Components/SwiftUI view componentsBrowser/, Sidebar/, Extensions/
Nook/Protocols/Protocol definitionsTabListDataSource
Nook/Utils/Utilities, WebKit extensions, Metal shadersWebKit/, Shaders/, Debug/
Nook/ThirdParty/Embedded dependenciesBigUIPaging, swift-atomics
Settings/Settings moduleSettings screens
CommandPalette/Command palette UIQuick action interface
UI/Shared UI componentsReusable SwiftUI views
Nook embeds all dependencies locally in Nook/ThirdParty/ — no Swift Package Manager resolution needed.

BrowserManager: The central coordinator

BrowserManager (Nook/Managers/BrowserManager/BrowserManager.swift:18) is currently a ~2800-line “god object” that connects all managers. It’s being incrementally refactored toward independent managers. Current responsibilities:
  • Owns all feature managers (TabManager, ProfileManager, ExtensionManager, etc.)
  • Coordinates tab/space/profile operations
  • Handles window lifecycle callbacks
  • Routes commands from menu bar and keyboard shortcuts
Refactoring plan:
  • Managers will become fully independent
  • Environment injection will replace direct references
  • BrowserManager will eventually be eliminated
// Current (legacy)
@EnvironmentObject var browserManager: BrowserManager
browserManager.tabManager.createTab(...)

// Future (independent managers)
@Environment(TabManager.self) private var tabManager
tabManager.createTab(...)

Persistence architecture

Nook uses SwiftData for persistent storage:
  • Schema defined in BrowserManager.swift:29-37:
    • SpaceEntity, ProfileEntity, TabEntity, FolderEntity
    • HistoryEntity, ExtensionEntity, TabsStateEntity
  • Atomic persistence via PersistenceActor (TabManager/TabManager.swift:12) — Coalesced writes with backup recovery
  • Data isolation via WKWebsiteDataStore per profile (Profile.swift:82-98)
See State management for details on persistence patterns.

Key design patterns

Tabs exist without loaded webviews to save memory. Tab.webView is lazily initialized on first access (Tab.swift:18).
// Tab can exist without webview
let tab = Tab(url: url, name: "Example")

// Webview created on first access
let webView = tab.webView // Triggers lazy init
When the same tab appears in multiple windows, WebViewCoordinator creates separate webview instances:
  • Primary webview: Owned by the tab, created for the first window
  • Clone webviews: Additional instances for other windows
All share the same configuration for extension compatibility (WebViewCoordinator.swift:96-131).
Each Profile owns a unique WKWebsiteDataStore for complete data separation:
  • Persistent profiles: Use WKWebsiteDataStore(forIdentifier: profileId)
  • Ephemeral profiles: Use WKWebsiteDataStore.nonPersistent()
Cookies, localStorage, and IndexedDB are isolated per profile (Profile.swift:82-98).
TabManager uses a Swift actor (PersistenceActor) for thread-safe, coalesced writes:
  1. Snapshot current state (tabs, spaces, folders)
  2. Create atomic transaction in child ModelContext
  3. On failure, fall back to best-effort save or restore from backup
This prevents data corruption during crashes (TabManager.swift:89-124).

Next steps

Managers

Deep dive into the manager pattern and individual manager responsibilities

Models

Explore data models and SwiftData entities

State management

Learn about @Observable vs @Published patterns and persistence

WebView coordination

Understand lazy init, multi-window cloning, and BrowserConfig

Build docs developers (and LLMs) love