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:Environment injection flow
The dependency graph looks like this (fromNookApp.swift:38-86):
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
- First window gets the “primary” webview from
Tab.webView - Additional windows get “clone” webviews created by
WebViewCoordinator.createCloneWebView()(WebViewCoordinator.swift:149) - All webviews share the same
WKWebViewConfigurationfor extension/process pool consistency
Top-level modules
Nook’s codebase is organized into these directories:| Directory | Purpose | Example Files |
|---|---|---|
App/ | Entry point, window management, app lifecycle | NookApp.swift, ContentView.swift, AppDelegate.swift |
Nook/Managers/ | ~27 feature managers (business logic) | BrowserManager/, TabManager/, ProfileManager/ |
Nook/Models/ | Data models and SwiftData entities | Tab/, Space/, Profile/, BrowserConfig/ |
Nook/Components/ | SwiftUI view components | Browser/, Sidebar/, Extensions/ |
Nook/Protocols/ | Protocol definitions | TabListDataSource |
Nook/Utils/ | Utilities, WebKit extensions, Metal shaders | WebKit/, Shaders/, Debug/ |
Nook/ThirdParty/ | Embedded dependencies | BigUIPaging, swift-atomics |
Settings/ | Settings module | Settings screens |
CommandPalette/ | Command palette UI | Quick action interface |
UI/ | Shared UI components | Reusable 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
- Managers will become fully independent
- Environment injection will replace direct references
- BrowserManager will eventually be eliminated
Persistence architecture
Nook uses SwiftData for persistent storage:-
Schema defined in
BrowserManager.swift:29-37:SpaceEntity,ProfileEntity,TabEntity,FolderEntityHistoryEntity,ExtensionEntity,TabsStateEntity
-
Atomic persistence via
PersistenceActor(TabManager/TabManager.swift:12) — Coalesced writes with backup recovery -
Data isolation via
WKWebsiteDataStoreper profile (Profile.swift:82-98)
Key design patterns
Lazy webview initialization
Lazy webview initialization
Tabs exist without loaded webviews to save memory.
Tab.webView is lazily initialized on first access (Tab.swift:18).Multi-window webview cloning
Multi-window webview cloning
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
WebViewCoordinator.swift:96-131).Profile data isolation
Profile data isolation
Each
Profile owns a unique WKWebsiteDataStore for complete data separation:- Persistent profiles: Use
WKWebsiteDataStore(forIdentifier: profileId) - Ephemeral profiles: Use
WKWebsiteDataStore.nonPersistent()
Profile.swift:82-98).Atomic snapshot persistence
Atomic snapshot persistence
TabManager uses a Swift actor (PersistenceActor) for thread-safe, coalesced writes:- Snapshot current state (tabs, spaces, folders)
- Create atomic transaction in child
ModelContext - On failure, fall back to best-effort save or restore from backup
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