How FluidVoice Manages Custom Dictation Prompts: From Legacy Strings to Profile Collections
FluidVoice manages custom dictation prompts through a migration-aware architecture that converts legacy customDictationPrompt strings into structured DictationPromptProfile objects stored in SettingsStore, enabling multi-profile support while maintaining backward compatibility.
FluidVoice implements a robust persistence strategy for user-defined dictation prompts that transitions from legacy single-string storage to a modern profile-based system. As implemented in altic-dev/FluidVoice, the application automatically migrates existing prompts into DictationPromptProfile collections while preserving user data, with all UI components and transcription services accessing prompts exclusively through the new profile architecture.
Legacy Storage and Migration Strategy
The Original customDictationPrompt Property
Historically, FluidVoice stored user prompts as plain strings using the customDictationPrompt property in SettingsStore. This property reads from and writes directly to UserDefaults, providing simple persistence for a single custom prompt text. You can find this legacy accessor in the persistence layer at line 2745. Source
Automatic Migration to Profile Collections
To support multiple prompt profiles without breaking existing user data, SettingsStore implements migrateDictationPromptProfilesIfNeeded(). This method executes during app initialization to check for legacy prompt data. If a legacy string exists, the system creates a new DictationPromptProfile instance named "My Custom Prompt", populates the dictationPromptProfiles array, and clears the deprecated customDictationPrompt value to prevent duplicate data. This migration logic is implemented at line 3065. Source
Modern Profile Architecture
DictationPromptProfile Model Structure
Each prompt is now represented as a DictationPromptProfile containing a unique identifier, human-readable name, prompt text content, and creation/update timestamps. This structure enables users to maintain multiple contextual prompts for different writing scenarios, such as formal documentation versus casual dictation, without overwriting previous configurations.
Profile Selection via selectedDictationPromptID
The active prompt is determined by the selectedDictationPromptID property, which stores the UUID of the currently selected profile. When the transcription engine requires a prompt, it queries SettingsStore.shared for the profile matching this ID and extracts the prompt value. This indirection allows seamless switching between prompt contexts without migrating data or restarting the application.
UI and Runtime Integration
Managing Prompts in CustomDictionaryView
Despite the file name suggesting dictionary functionality, CustomDictionaryView.swift serves as the primary interface for prompt profile management. The view presents the dictationPromptProfiles collection from SettingsStore, handles creation and deletion of profiles, and updates selectedDictationPromptID when users switch between active prompts.
PromptTextView and Real-time Display
PromptTextView.swift renders the active prompt text within the user interface, observing changes to selectedDictationPromptID through the settings store to ensure the displayed content reflects the currently selected profile without requiring manual refresh.
ASRService Consumption
When building transcription requests, ASRService.swift retrieves the active profile's prompt text from SettingsStore.shared.selectedDictationPromptID, injecting the contextual instructions into the request payload sent to the back-end transcription service. This ensures that dictation context travels with each speech recognition request.
Practical Implementation Examples
Accessing prompts through the modern profile API ensures compatibility with current and future FluidVoice features:
// Legacy access pattern - maintained only for migration compatibility
let legacyText = SettingsStore.shared.customDictationPrompt
// Modern approach: Retrieve the active profile
if let activeID = SettingsStore.shared.selectedDictationPromptID,
let profile = SettingsStore.shared.dictationPromptProfiles.first(where: { $0.id == activeID }) {
let promptText = profile.prompt
// Inject promptText into transcription request
}
// Creating a new profile programmatically
let projectProfile = DictationPromptProfile(
name: "Technical Documentation",
prompt: "You are an expert technical writer specializing in API documentation...",
createdAt: Date(),
updatedAt: Date()
)
SettingsStore.shared.dictationPromptProfiles.append(projectProfile)
SettingsStore.shared.selectedDictationPromptID = projectProfile.id
Summary
- Legacy Migration: FluidVoice automatically converts legacy
customDictationPromptstrings into structuredDictationPromptProfileobjects viamigrateDictationPromptProfilesIfNeeded()to preserve user data during upgrades. - Profile Storage: Prompts are stored as
DictationPromptProfileinstances containing metadata and timestamps, managed within thedictationPromptProfilesarray inSettingsStore. - Selection Mechanism: The active prompt is tracked by
selectedDictationPromptID, allowing runtime switching between multiple profiles without data loss. - UI Integration:
CustomDictionaryViewandPromptTextViewprovide the interface for profile management, whileASRServiceconsumes the active prompt for transcription requests.
Frequently Asked Questions
How does FluidVoice handle existing custom prompts when upgrading to the profile system?
When the app launches, SettingsStore.migrateDictationPromptProfilesIfNeeded() checks for legacy customDictationPrompt data. If present, it automatically creates a new profile named "My Custom Prompt" containing the existing text, adds it to the dictationPromptProfiles collection, and clears the legacy field to prevent duplication.
Where are custom dictation prompts physically stored?
Prompt profiles are persisted to UserDefaults through the SettingsStore singleton. Each DictationPromptProfile is encoded and stored in the dictationPromptProfiles array, while the active selection reference (selectedDictationPromptID) is stored separately to enable quick lookup without decoding the entire collection.
Can I have multiple custom dictation prompts active simultaneously?
While only one profile can be active at a time (determined by selectedDictationPromptID), you can create unlimited profiles in the dictationPromptProfiles array. The UI in CustomDictionaryView allows instant switching between profiles, making it easy to change dictation contexts for different projects or writing styles.
What happens if the selected profile is deleted?
The code implementation requires careful handling of deletion events. When a profile is removed from dictationPromptProfiles, the system should validate that selectedDictationPromptID does not reference a deleted UUID. If the active profile is deleted, the selection typically falls back to the first available profile or a default system prompt, though specific fallback behavior depends on the validation logic in SettingsStore.
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 →