# How FluidVoice Manages Custom Dictation Prompts: From Legacy Strings to Profile Collections

> Discover how FluidVoice expertly manages custom dictation prompts by migrating legacy strings to structured profile collections. Learn about backward compatibility and multi-profile support for seamless integration in your proj...

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: internals
- Published: 2026-07-07

---

**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](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Persistence/SettingsStore.swift#L2745)

### 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](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Persistence/SettingsStore.swift#L3065)

## 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`](https://github.com/altic-dev/FluidVoice/blob/main/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`](https://github.com/altic-dev/FluidVoice/blob/main/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`](https://github.com/altic-dev/FluidVoice/blob/main/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:

```swift
// 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 `customDictationPrompt` strings into structured `DictationPromptProfile` objects via `migrateDictationPromptProfilesIfNeeded()` to preserve user data during upgrades.
- **Profile Storage:** Prompts are stored as `DictationPromptProfile` instances containing metadata and timestamps, managed within the `dictationPromptProfiles` array in `SettingsStore`.
- **Selection Mechanism:** The active prompt is tracked by `selectedDictationPromptID`, allowing runtime switching between multiple profiles without data loss.
- **UI Integration:** `CustomDictionaryView` and `PromptTextView` provide the interface for profile management, while `ASRService` consumes 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`.