# How to Configure CodexBar Settings: A Complete Guide to SettingsStore and Config Persistence

> Configure CodexBar settings effectively using SettingsStore. Learn how to manage UI preferences and provider configurations with automatic persistence for the steipete/CodexBar repository.

- Repository: [Peter Steinberger/CodexBar](https://github.com/steipete/CodexBar)
- Tags: how-to-guide
- Published: 2026-07-07

---

**You configure CodexBar settings through the centralized `SettingsStore` class, which synchronizes UI preferences to `UserDefaults` and provider-specific configurations to a JSON-backed `CodexBarConfig` file, automatically persisting changes with a 350ms debounce.**

To configure CodexBar settings effectively, you need to understand how the open-source macOS menu-bar application steipete/CodexBar handles configuration persistence. The app uses a dual-layer storage system managed by the `SettingsStore` observable object, ensuring that both interface preferences and provider credentials remain synchronized between launches.

## Understanding CodexBar's Dual-Layer Configuration System

CodexBar splits its configuration across two distinct storage mechanisms to separate transient UI state from structural provider data.

### UserDefaults for UI Preferences

All user-facing interface settings—such as **refresh frequency**, **menu bar display mode**, and **quota warning toggles**—live in `UserDefaults`. The `SettingsStore` class bootstraps these values through the `loadDefaultsState(userDefaults:)` method in [`Sources/CodexBar/SettingsStore.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/SettingsStore.swift) (lines 73-124). This helper extracts values with fallbacks and performs migrations, such as converting legacy keys like `menuBarMetricPreference` into newer dictionary forms.

### CodexBarConfig for Provider Settings

Provider-specific configuration—including enabled providers, token accounts, and provider ordering—resides in a typed `CodexBarConfig` model. This structure is persisted as JSON on disk and managed through `Sources/CodexBar/SettingsStore+ConfigPersistence.swift` (lines 36-84). The `CodexBarConfigStore` handles the actual file I/O, while `SettingsStore` maintains the runtime model.

## Reading and Initializing Configuration Settings

When CodexBar launches, `SettingsStore` initializes its state by calling `loadDefaultsState`, which returns a `SettingsDefaultsState` struct containing concrete values for all UI preferences. This method runs migrations to ensure backward compatibility with older configuration formats.

For example, the refresh frequency setting is loaded with a fallback to five minutes:

```swift
let refreshFrequency = userDefaults.string(forKey: "refreshFrequency")
    .flatMap(RefreshFrequency.init(rawValue:))
    ?? .fiveMinutes

```

If you are running tests, this method also seeds `UserDefaults` with deterministic values to ensure consistent behavior across test runs.

## How to Update CodexBar Settings Programmatically

All mutable settings funnel through the `updateConfig(reason:mutate:)` method. For provider-specific changes, use `updateProviderConfig`, which builds a new `CodexBarConfig`, updates the in-memory state, and triggers `schedulePersistConfig`.

### Changing Refresh Frequency

To modify how often CodexBar polls for usage data, set the `refreshFrequency` property on the shared store:

```swift
let store = SettingsStore.shared
store.refreshFrequency = .twoMinutes

```

This writes directly to the `UserDefaults` key `refreshFrequency` and updates the polling timer immediately.

### Enabling and Ordering Providers

To enable a specific provider like Claude or OpenAI, use `setProviderEnabled(provider:metadata:enabled:)`, then reorder providers with `setProviderOrder`:

```swift
// Enable OpenAI
store.setProviderEnabled(
    provider: .openAI,
    metadata: ProviderDescriptorRegistry.metadata[.openAI]!,
    enabled: true
)

// Reorder to prioritize OpenAI first
store.setProviderOrder([.openAI, .claude, .gemini])

```

These changes modify the `CodexBarConfig.providers` array and persist to disk automatically.

### Toggling UI Features

Menu bar display preferences are exposed through the `defaultsState` property:

```swift
// Disable the "highest usage" badge
store.defaultsState.menuBarShowsHighestUsage = false

// Change display mode to show percentage on the left
store.menuBarDisplayMode = .percentLeft

```

## Persisting Configuration Changes to Disk

`SettingsStore` does not write to disk immediately on every change. Instead, it uses `schedulePersistConfig` to debounce writes by 350 milliseconds. This method cancels any pending write operation and schedules a new asynchronous save of the full `CodexBarConfig` to JSON.

As implemented in `Sources/CodexBar/SettingsStore+ConfigPersistence.swift` (lines 49-81), the persistence logic handles both asynchronous writes during normal operation and synchronous writes during test execution.

## Reloading Configuration from External Sources

If an external process modifies the configuration file (such as during iCloud sync), you can force a reload without restarting the app:

```swift
store.reloadConfig(reason: "external-sync")

```

This method calls `CodexBarConfigStore` to load the JSON from disk, then uses `applyExternalConfig` to swap in the new model. The revision counter increments, but `ConfigChangeContext.shouldBroadcast` prevents unnecessary UI refresh broadcasts when the change originated externally.

## Summary

- **CodexBar uses two storage layers**: `UserDefaults` for UI preferences and a JSON-backed `CodexBarConfig` for provider settings.
- **`SettingsStore`** in [`Sources/CodexBar/SettingsStore.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/SettingsStore.swift) serves as the central observable object that binds SwiftUI views to configuration state.
- **All mutations** route through `updateConfig` or `updateProviderConfig`, ensuring type safety and automatic persistence.
- **Disk writes are debounced** by 350ms via `schedulePersistConfig` to prevent excessive I/O during rapid setting changes.
- **Configuration is round-trippable**: changes made programmatically or via the preferences pane reflect immediately in the UI and survive app restarts.

## Frequently Asked Questions

### Where does CodexBar store its settings on disk?

CodexBar stores UI preferences in `UserDefaults` under standard app suite identifiers, while provider configurations (tokens, enabled states, ordering) serialize to a JSON file managed by `CodexBarConfigStore` in `Sources/CodexBar/SettingsStore+ConfigPersistence.swift`. The exact file path depends on your app's container directory, typically within `~/Library/Application Support/` or the shared App Group container.

### How do I change the refresh interval in CodexBar?

Set the `refreshFrequency` property on `SettingsStore.shared` to a valid `RefreshFrequency` enum case such as `.oneMinute`, `.twoMinutes`, or `.fiveMinutes`. This updates the `UserDefaults` key `refreshFrequency` immediately and reschedules the background polling timer without requiring an app restart.

### What is the SettingsStore class in CodexBar?

`SettingsStore` is an `@Observable` singleton-style class defined in [`Sources/CodexBar/SettingsStore.swift`](https://github.com/steipete/CodexBar/blob/main/Sources/CodexBar/SettingsStore.swift) that acts as the single source of truth for all configuration state. It exposes SwiftUI-friendly properties, handles legacy migrations, coordinates persistence through `SettingsStore+ConfigPersistence.swift`, and provides methods like `setProviderEnabled` and `reloadConfig` for programmatic configuration management.

### How long does CodexBar wait before saving configuration changes?

CodexBar debounces configuration writes by **350 milliseconds** using `schedulePersistConfig`. When you modify any setting, the store cancels any pending write, waits for 350ms of inactivity, then persists the full `CodexBarConfig` to disk asynchronously. This prevents excessive disk I/O when multiple settings change in rapid succession.