diff --git a/VoiceInk/Services/DictionaryImportExportService.swift b/VoiceInk/Services/DictionaryImportExportService.swift index ae1407e..20f4415 100644 --- a/VoiceInk/Services/DictionaryImportExportService.swift +++ b/VoiceInk/Services/DictionaryImportExportService.swift @@ -27,7 +27,8 @@ class DictionaryImportExportService { var wordReplacements: [String: String] = [:] let replacementsDescriptor = FetchDescriptor() if let replacements = try? context.fetch(replacementsDescriptor) { - wordReplacements = Dictionary(uniqueKeysWithValues: replacements.map { ($0.originalText, $0.replacementText) }) + // Use uniquingKeysWith to handle potential duplicates gracefully (keep first occurrence) + wordReplacements = Dictionary(replacements.map { ($0.originalText, $0.replacementText) }, uniquingKeysWith: { first, _ in first }) } let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0.0" @@ -121,7 +122,6 @@ class DictionaryImportExportService { let importedWords = self.extractWords(from: normalizedImportedKey) // Check for conflicts and update existing replacements - var hasConflict = false for existingReplacement in existingReplacements { var existingWords = self.extractWords(from: existingReplacement.originalText) var modified = false @@ -130,7 +130,6 @@ class DictionaryImportExportService { if let index = existingWords.firstIndex(where: { $0.lowercased() == importedWord.lowercased() }) { existingWords.remove(at: index) modified = true - hasConflict = true } } diff --git a/VoiceInk/Services/ImportExportService.swift b/VoiceInk/Services/ImportExportService.swift index 9bfdad4..386c2bb 100644 --- a/VoiceInk/Services/ImportExportService.swift +++ b/VoiceInk/Services/ImportExportService.swift @@ -235,9 +235,34 @@ class ImportExportService { // Import word replacements to SwiftData if let replacementsToImport = importedSettings.wordReplacements { + let replacementsDescriptor = FetchDescriptor() + let existingReplacements = (try? whisperState.modelContext.fetch(replacementsDescriptor)) ?? [] + + // Build a set of existing replacement keys for duplicate checking + var existingKeysSet = Set() + for existing in existingReplacements { + let tokens = existing.originalText + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() } + .filter { !$0.isEmpty } + existingKeysSet.formUnion(tokens) + } + for (original, replacement) in replacementsToImport { - let newReplacement = WordReplacement(originalText: original, replacementText: replacement) - whisperState.modelContext.insert(newReplacement) + let importTokens = original + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() } + .filter { !$0.isEmpty } + + // Check if any token already exists + let hasConflict = importTokens.contains { existingKeysSet.contains($0) } + + if !hasConflict { + let newReplacement = WordReplacement(originalText: original, replacementText: replacement) + whisperState.modelContext.insert(newReplacement) + // Add these tokens to the set to prevent duplicates within the import + existingKeysSet.formUnion(importTokens) + } } try? whisperState.modelContext.save() print("Successfully imported word replacements to SwiftData.")