What Is the Role of AppPreferences.swift in Managing User Settings?
AppPreferences.swift serves as the centralized, type-safe singleton that persists and retrieves all user-configurable settings in OpenSuperWhisper through a clean UserDefaults API.
In the OpenSuperWhisper macOS transcription app, the role of AppPreferences.swift in managing user settings is centralized through a single source of truth rather than scattered UserDefaults calls. This file defines a singleton hub, located at OpenSuperWhisper/Utils/AppPreferences.swift, that wraps Foundation’s preferences system with compile-time type safety and automatic migration logic.
Singleton Architecture for Global Access
The file exposes one shared instance via static let shared = AppPreferences(). This guarantees every module reads the same underlying state without creating multiple UserDefaults managers.
Other components access values directly through this singleton. For example, reading the default transcription language looks like this:
let language = AppPreferences.shared.whisperLanguage // → "en" by default
Updates are equally direct and immediately persist:
AppPreferences.shared.translateToEnglish = true
Automatic Migration of Legacy Keys
Backward compatibility is handled internally by the migrateOldPreferences() method. When the app launches, this method transparently moves legacy keys—such as the older selectedModelPath—to the newer selectedWhisperModelPath key.
As implemented in Starmel/OpenSuperWhisper, this migration ensures existing users retain their configuration after updates without manual intervention.
Type-Safe Property Wrappers
Instead of raw string keys and manual casting, AppPreferences.swift declares properties through custom wrappers that enforce type safety.
@UserDefault for Non-Optional Values
The @UserDefault wrapper stores non-optional values and supplies a default fallback when the key is absent. This eliminates boilerplate UserDefaults.standard.object(forKey:) calls and enforces compile-time correctness.
@OptionalUserDefault for Optional Values
The @OptionalUserDefault wrapper stores optional values, returning nil when no value has been persisted. This pattern is used for data that may not exist on first launch, such as microphone calibration:
let data: Data = … // encoded microphone data
AppPreferences.shared.selectedMicrophoneData = data // stored as optional
Domain-Specific Preference Grouping
Preferences inside AppPreferences.swift are logically grouped by functional domain. The file organizes properties for engine selection, model paths, transcription parameters, clipboard behavior, and hotkey configuration.
This grouping makes it easy for developers to discover relevant settings. A UI panel in OpenSuperWhisper/Settings.swift writes to these grouped properties, while backend services read only the subsets they require.
Dynamic Derived Properties
Some properties compute their return value based on the active engine. The selectedModelPath property is a derived accessor that abstracts the underlying engine-specific path. When the Whisper engine is active, it delegates to selectedWhisperModelPath:
if let modelPath = AppPreferences.shared.selectedModelPath {
// Feed `modelPath` into the WhisperEngine initializer
}
This layer of indirection keeps caller code decoupled from engine-specific storage keys.
Integration with the Rest of the App
The singleton is consumed throughout the repository. OpenSuperWhisper/TranscriptionService.swift reads engine and model preferences to initialize transcription pipelines. OpenSuperWhisper/ShortcutManager.swift retrieves hotkey configuration through the same shared instance:
let modifier = ModifierKey(rawValue: AppPreferences.shared.modifierOnlyHotkey) ?? .none
let mouseBtn = MouseButton(rawValue: AppPreferences.shared.mouseButtonHotkey) ?? .none
Even the app lifecycle layer in OpenSuperWhisper/OpenSuperWhisperApp.swift persists onboarding completion flags via AppPreferences.shared, confirming that every layer of the app relies on this single settings backbone.
Summary
AppPreferences.swiftprovides a singleton (AppPreferences.shared) that acts as the sole source of truth for user settings in OpenSuperWhisper.- The
migrateOldPreferences()method handles legacy key migration automatically. - Custom property wrappers (
@UserDefaultand@OptionalUserDefault) enforce compile-time type safety and removeUserDefaultsboilerplate. - Settings are grouped by domain, making the API discoverable for UI and service layers.
- Derived properties like
selectedModelPathabstract engine-specific storage behind a unified interface.
Frequently Asked Questions
How does AppPreferences.swift persist data across app launches?
It writes every property to UserDefaults through custom property wrappers. Because UserDefaults is backed by a plist on macOS, values survive app restarts and are available immediately on the next launch via AppPreferences.shared.
What is the difference between @UserDefault and @OptionalUserDefault?
@UserDefault is designed for non-optional values and guarantees a fallback default, while @OptionalUserDefault stores optional values and returns nil when the key has never been set. Both wrappers handle the underlying UserDefaults read and write logic automatically.
Which OpenSuperWhisper components depend on AppPreferences.shared?
Core modules including OpenSuperWhisper/TranscriptionService.swift, OpenSuperWhisper/ShortcutManager.swift, and OpenSuperWhisper/Settings.swift all read or write values through AppPreferences.shared. This shared access guarantees a consistent configuration state across the entire application.
How does the app handle old preference keys after updates?
The migrateOldPreferences() method inside AppPreferences.swift transparently remaps legacy keys. For example, it migrates the older selectedModelPath to the newer selectedWhisperModelPath, so existing users retain their settings without manual migration steps.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →