# How Settings Are Managed and Persisted in vorssaint-utils: A Complete Technical Guide

> Discover how vorssaint-utils manages and persists settings using macOS UserDefaults, type-safe keys, and a helper Defaults struct. Learn the complete technical guide.

- Repository: [vorssaint/vorssaint-utils](https://github.com/vorssaint/vorssaint-utils)
- Tags: deep-dive
- Published: 2026-09-09

---

**vorssaint-utils persists all user preferences using macOS UserDefaults, accessed through type-safe keys defined in a central `DefaultsKey` enum and sanitized via a helper `Defaults` struct.**

The vorssaint-utils macOS application implements a robust persistence layer for user-configurable preferences. By wrapping Apple's `UserDefaults` API with a consistent key-naming strategy and data sanitization utilities, the codebase ensures that settings remain consistent across sessions while remaining easy to maintain and test.

## The UserDefaults Architecture in vorssaint-utils

The architecture isolates raw storage behind well-named keys and helper methods, ensuring that every component reads and writes preferences in a consistent, testable way. All settings are persisted automatically by the OS and restored on next launch, providing a seamless user experience.

### Type-Safe Key Definitions with DefaultsKey

At the core of the system lies a central enum or struct named `DefaultsKey` that defines string constants for every preference option. This approach keeps the key space consistent and prevents string-typo bugs across the codebase.

```swift
// Example key definition
DefaultsKey.windowPreviewExcludedApps
DefaultsKey.windowGestureEnabled
DefaultsKey.radialMenuProfiles

```

### Standard vs. Suite-Based Storage

Most components access preferences through `UserDefaults.standard`, the shared defaults object for the current user. However, the codebase also supports named suites via `UserDefaults(suiteName: ...)` for specific migration scenarios or sandboxed components, such as when interacting with `com.apple.WindowManager`.

## The Settings Persistence Workflow

The repository follows a predictable five-step workflow for managing user preferences, from definition to UI observation.

### Defining and Reading Preferences

UI components and services read values using the standard UserDefaults API, often providing fallback defaults when keys are missing. In [`Sources/Vorssaint/UI/Settings/WindowPreviewExclusionsList.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/Settings/WindowPreviewExclusionsList.swift), the application retrieves excluded applications using:

```swift
// Read a boolean preference
let gesturesEnabled = UserDefaults.standard.bool(forKey: DefaultsKey.windowGestureEnabled)

// Read an array with fallback to empty list
let excludedApps = UserDefaults.standard.stringArray(forKey: DefaultsKey.windowPreviewExcludedApps) ?? []

```

### Data Sanitization

Before persisting or using raw UserDefaults data, the `Defaults` helper struct centralizes common logic to clean values. For bundle identifiers, `Defaults.sanitizedBundleIdentifierList(...)` removes empty strings and invalid entries, ensuring downstream components work with clean data:

```swift
let sanitized = Defaults.sanitizedBundleIdentifierList(bundleIDs)

```

### Writing Values Back to Storage

Updates flow back through the standard API. In [`Sources/Vorssaint/UI/Settings/ShortcutsSettings.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/Settings/ShortcutsSettings.swift), sanitized data is persisted using:

```swift
func saveExcludedApps(_ bundleIDs: [String]) {
    let sanitized = Defaults.sanitizedBundleIdentifierList(bundleIDs)
    UserDefaults.standard.set(sanitized, forKey: DefaultsKey.windowPreviewExcludedApps)
}

```

### Observing Changes in SwiftUI

Views refresh automatically using `@State` or `@ObservedObject` wrappers that initialize from saved values. When the underlying UserDefaults value changes, the UI updates to reflect the new state without requiring manual notification handling.

## Handling Different Data Types

The implementation handles primitives, collections, and complex objects with specific patterns for each category.

### Boolean Toggles

Simple on/off preferences use the native boolean API. The window-gesture service throughout the codebase checks flags using:

```swift
let enabled = UserDefaults.standard.bool(forKey: DefaultsKey.windowGestureEnabled)

```

Values are written using `set(_:forKey:)` with the appropriate `DefaultsKey` constant.

### Collections and Property Lists

Array and dictionary values store as property-list-compatible collections. The window preview exclusion list manages arrays of bundle identifier strings:

```swift
// Retrieval
let apps = UserDefaults.standard.stringArray(forKey: DefaultsKey.windowPreviewExcludedApps) ?? []

// Persistence
UserDefaults.standard.set(sanitizedArray, forKey: DefaultsKey.windowPreviewExcludedApps)

```

### Complex Codable Objects

For structured data like radial-menu profiles, the application encodes objects to `Data` using `Codable` helpers before storage. In [`Sources/Vorssaint/UI/Settings/RadialMenuSettings.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/Sources/Vorssaint/UI/Settings/RadialMenuSettings.swift):

```swift
// Encoding and saving
let encoded = RadialMenuSupport.encodeProfiles(profiles)
UserDefaults.standard.set(encoded, forKey: DefaultsKey.radialMenuProfiles)

// Retrieval and decoding
if let data = UserDefaults.standard.data(forKey: DefaultsKey.radialMenuProfiles) {
    let profiles = RadialMenuSupport.decodeProfiles(data)
}

```

## Key Implementation Files

Several source files demonstrate the unified persistence pattern across different subsystems:

- **Sources/Vorssaint/UI/Settings/WindowPreviewExclusionsList.swift** – Manages the window-preview exclusion list UI, reading and writing bundle ID arrays via `UserDefaults.standard`.

- **Sources/Vorssaint/UI/Settings/ShortcutsSettings.swift** – Handles shortcut-related toggles, implementing read/write cycles for keyboard preference changes.

- **Sources/Vorssaint/UI/Settings/RadialMenuSettings.swift** – Persists radial-menu profiles as encoded `Data` objects using `RadialMenuSupport` encoding helpers.

- **Sources/Vorssaint/Services/Update/UpdateService.swift** – Stores auto-check and beta-update flags as boolean values in standard defaults.

- **Sources/Vorssaint/Services/WindowLayout/WindowLayoutSupport.swift** – Reads layout spacing values and shortcut strings from persistent storage on initialization.

- **Sources/Vorssaint/Support/Uninstaller.swift** – Checks a persistent flag via `UserDefaults` to determine whether to skip system sleep during cleanup operations.

## Summary

- **vorssaint-utils** uses **macOS UserDefaults** as the underlying persistence mechanism for all user preferences.
- A central **`DefaultsKey`** enum provides type-safe string constants, preventing key-naming errors across the codebase.
- The **`Defaults`** helper struct encapsulates sanitization logic, ensuring that raw UserDefaults data is cleaned before use.
- **Complex objects** are persisted by encoding them to `Data` via `Codable` protocols, while primitives and arrays use native UserDefaults methods.
- The architecture supports both **standard** defaults and **named suites** for migration or sandboxing scenarios.
- Implementation files in `Sources/Vorssaint/UI/Settings/` and `Sources/Vorssaint/Services/` demonstrate consistent read-write-observe patterns throughout the application.

## Frequently Asked Questions

### Where does vorssaint-utils store user preferences?

vorssaint-utils stores all user-configurable preferences in the macOS **UserDefaults** system. Most settings use `UserDefaults.standard`, while specific components may use named suites via `UserDefaults(suiteName: ...)` for sandboxing or migration purposes. The OS handles persistence automatically, requiring no manual file management.

### How does the app ensure type safety when accessing UserDefaults?

The application defines a central enum or struct called **`DefaultsKey`** that exports string constants for every preference. By requiring developers to use these constants instead of raw strings, the codebase prevents typos and provides compile-time checking. All access flows through these keys, ensuring consistent reads and writes across `Sources/Vorssaint/UI/Settings/` and service layers.

### Can vorssaint-utils settings be migrated between different UserDefaults suites?

Yes. While the majority of the codebase uses `UserDefaults.standard`, specific components utilize suite-based initialization with `UserDefaults(suiteName: "com.apple.WindowManager")` for migration or sandboxed storage scenarios. This allows the application to interact with system-level preferences or transition data between different containers when necessary.

### How are complex objects like radial menu profiles persisted?

Complex objects are encoded to `Data` using `Codable` protocol implementations before storage. For example, `RadialMenuSupport.encodeProfiles(profiles)` converts profile objects to data that `UserDefaults.standard.set(_:forKey:)` can store. Retrieval reverses this process: fetching the `Data` value and decoding it through `RadialMenuSupport.decodeProfiles(data)`. This pattern appears in [`RadialMenuSettings.swift`](https://github.com/vorssaint/vorssaint-utils/blob/main/RadialMenuSettings.swift) and other settings files handling non-primitive types.