Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/0xdps/default-tamer/llms.txt

Use this file to discover all available pages before exploring further.

Default Tamer is built with SwiftUI and follows a clear separation of concerns. This guide explains the project structure and key components.

Project Structure

The main source code is organized in the DefaultTamer/ directory:
DefaultTamer/
├── Models/           # Data models
├── Services/         # Business logic
├── Views/            # SwiftUI views
├── Utilities/        # Helper functions
├── AppDelegate.swift
└── DefaultTamerApp.swift

Core Components

Models

Data structures representing core concepts:
// Represents an installed browser
struct Browser {
    let id: String          // Bundle ID
    let displayName: String
    let icon: NSImage?
}

Models Directory

Also includes:
  • AppError.swift - Error types
  • GitHubRelease.swift - Update checking

Services

Business logic and core functionality:

AppState.swift

Central state management for the entire app:
AppState.swift:13-31
@MainActor
class AppState: ObservableObject {
    // Managers
    let browserManager = BrowserManager()
    let diagnosticsManager = DiagnosticsManager()
    let persistence = PersistenceManager.shared
    let toastManager = ToastManager.shared
    
    // Published state
    @Published var settings: Settings
    @Published private(set) var rules: [Rule]
    @Published var showFirstRun: Bool
    @Published var showRulesWindow = false
    @Published var showChooser = false
    @Published var chooserURL: URL?
    @Published var chooserSourceApp: String?
    @Published var pendingTabSelection: Int?
}
  • Manages app-wide settings and rules
  • Coordinates between different managers
  • Handles URL routing requests
  • Publishes state changes to views
  • Thread-safe rule management via dispatch queue
  1. Receives URL from AppDelegate
  2. Detects source application
  3. Routes through Router.route()
  4. Executes resulting action
  5. Logs activity if diagnostics enabled

Router.swift

Pure routing decision engine:
Router.swift:17-58
class Router {
    /// Main routing decision function
    static func route(
        url: URL,
        sourceApp: String?,
        settings: Settings,
        rules: [Rule],
        modifierFlags: NSEvent.ModifierFlags?
    ) -> RouteAction {
        // Check if app is disabled
        guard settings.enabled else {
            return .openInFallback
        }
        
        // Check for Option key to show chooser
        if let flags = modifierFlags, flags.contains(.option) {
            return .showChooser(url: url)
        }
        
        // Evaluate rules top-to-bottom, first match wins
        for rule in rules where rule.enabled {
            if let action = evaluateRule(rule, url: url, sourceApp: sourceApp) {
                return action
            }
        }
        
        // No rule matched, use fallback
        return .openInFallback
    }
}
1

Check app enabled

If app is disabled, route to fallback browser immediately.
2

Check modifier keys

If Option key (⌥) is held, show browser chooser.
3

Evaluate rules

Process rules top-to-bottom. First matching rule wins.
4

Apply fallback

If no rules match, open in fallback browser.

BrowserManager.swift

Discovers and manages installed browsers:
class BrowserManager {
    @Published var availableBrowsers: [Browser] = []
    
    func scanBrowsers()
    func openURL(_ url: URL, inBrowser bundleId: String)
    func openURLWithFallback(_ url: URL, 
                            targetBrowserId: String,
                            fallbackBrowserId: String,
                            privateMode: Bool)
    func isDefaultBrowser() -> Bool
}

Other Services

  • PersistenceManager.swift - UserDefaults storage
  • DiagnosticsManager.swift - Activity logging
  • SourceAppDetector.swift - Source app detection
  • ActivityDatabase.swift - SQLite logging
  • UpdateManager.swift - Sparkle updates

Views

SwiftUI views for the user interface:

MenuBarPopover.swift

Menu bar dropdown showing app status, preferences, and rules access

FirstRunView.swift

Onboarding window shown on first launch

PreferencesWindow.swift

Settings and configuration interface

RulesWindow.swift

Rule management interface with drag-to-reorder

BrowserChooser.swift

Manual browser selection dialog (shown when holding Option)

RecentRoutesView.swift

Activity log viewer (when diagnostics enabled)

Utilities

Helper functions and extensions:
  • UnifiedLogger.swift - Centralized logging
  • DebugLog.swift - Debug output helpers
  • Logging.swift - Log utilities
  • URLSanitizer.swift - Strips sensitive data from URLs
  • URLHelpers.swift - URL parsing and validation
  • RegexValidator.swift - Regex pattern validation
  • LaunchAtLogin.swift - Launch at login management
  • ApplicationScanner.swift - Finds installed apps
  • AppResolver.swift - Resolves bundle IDs to apps
  • ToastManager.swift - Toast notifications
  • ErrorNotification.swift - Error alerts
  • Color+Extensions.swift - Color helpers
  • Constants.swift - UI constants
  • RuleImportExport.swift - Rule backup/restore
  • ErrorHandler.swift - Error handling

Application Entry Points

DefaultTamerApp.swift

Main SwiftUI app structure:
@main
struct DefaultTamerApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        Settings {
            EmptyView()
        }
    }
}
The app uses NSApplicationDelegateAdaptor to integrate AppKit functionality while maintaining a SwiftUI structure.

AppDelegate.swift

AppKit delegate for system integration:
AppDelegate.swift:25-114
func applicationDidFinishLaunching(_ notification: Notification) {
    // Set as menu bar only app (no dock icon)
    NSApp.setActivationPolicy(.accessory)
    
    // Create menu bar status item
    statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    
    // Build menu with SwiftUI views
    menu = NSMenu()
    
    // Register for URL events
    NSAppleEventManager.shared().setEventHandler(
        self,
        andSelector: #selector(handleGetURLEvent(_:withReplyEvent:)),
        forEventClass: AEEventClass(kInternetEventClass),
        andEventID: AEEventID(kAEGetURL)
    )
    
    // Show first run if needed
    if appState.showFirstRun {
        showFirstRunWindow()
    }
}
1

Initialize menu bar

Creates status bar item with icon and menu
2

Register URL handler

Listens for kAEGetURL Apple Events to receive URLs
3

Setup window management

Manages preferences, first-run, and chooser windows
4

Handle URL events

Routes incoming URLs through the routing system

Data Flow

Here’s how a URL flows through the application:
1

URL interception

macOS sends URL to Default Tamer via Apple Event
2

Source detection

SourceAppDetector identifies which app sent the URL
3

Rule evaluation

Router evaluates rules top-to-bottom to find a match
4

Browser selection

Matched rule determines target browser (or uses fallback)
5

URL opening

BrowserManager opens URL in selected browser
6

Activity logging

If diagnostics enabled, logs the routing decision

Design Patterns

ObservableObject Pattern

AppState uses SwiftUI’s ObservableObject protocol:
@MainActor
class AppState: ObservableObject {
    @Published var settings: Settings
    @Published private(set) var rules: [Rule]
}
Views automatically update when published properties change.

Manager Pattern

Separate managers handle specific concerns:
  • BrowserManager - Browser discovery and launching
  • PersistenceManager - Data storage
  • DiagnosticsManager - Activity logging
  • UpdateManager - Software updates

Pure Functions

Router.route() is a pure function with no side effects:
  • Takes all needed data as parameters
  • Returns a decision (RouteAction)
  • Does not modify state or perform I/O
This makes routing logic testable and predictable.

Next Steps

Building

Learn how to build and run the app

Contributing

Read the contribution guidelines

Build docs developers (and LLMs) love