195 lines
8.6 KiB
Swift
195 lines
8.6 KiB
Swift
import Foundation
|
|
import os
|
|
|
|
class DataMigrationManager {
|
|
private let logger = Logger(
|
|
subsystem: "com.prakashjoshipax.VoiceInk",
|
|
category: "DataMigration"
|
|
)
|
|
|
|
static let shared = DataMigrationManager()
|
|
private let swiftDataMigrationKey = "hasPerformedSwiftDataMigration"
|
|
private let whisperModelsMigrationKey = "hasPerformedWhisperModelsMigration"
|
|
private let preferencesMigrationKey = "hasPerformedPreferencesMigration"
|
|
|
|
private init() {}
|
|
|
|
func performMigrationsIfNeeded() {
|
|
migratePreferencesIfNeeded() // Do preferences first as other migrations might need new preferences
|
|
migrateSwiftDataStoreIfNeeded()
|
|
migrateWhisperModelsIfNeeded()
|
|
}
|
|
|
|
private func migratePreferencesIfNeeded() {
|
|
// Check if migration has already been performed
|
|
if UserDefaults.standard.bool(forKey: preferencesMigrationKey) {
|
|
logger.info("Preferences migration already performed")
|
|
return
|
|
}
|
|
|
|
logger.info("Starting preferences migration")
|
|
|
|
let bundleId = "com.prakashjoshipax.VoiceInk"
|
|
|
|
// Old location (in Containers)
|
|
let oldPrefsURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0]
|
|
.appendingPathComponent("Containers/\(bundleId)/Data/Library/Preferences/\(bundleId).plist")
|
|
|
|
// New location
|
|
let newPrefsURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0]
|
|
.appendingPathComponent("Preferences/\(bundleId).plist")
|
|
|
|
do {
|
|
if FileManager.default.fileExists(atPath: oldPrefsURL.path) {
|
|
logger.info("Found old preferences at: \(oldPrefsURL.path)")
|
|
|
|
// Read old preferences
|
|
let oldPrefsData = try Data(contentsOf: oldPrefsURL)
|
|
if let oldPrefs = try PropertyListSerialization.propertyList(from: oldPrefsData, format: nil) as? [String: Any] {
|
|
|
|
// Migrate each preference to new UserDefaults
|
|
for (key, value) in oldPrefs {
|
|
UserDefaults.standard.set(value, forKey: key)
|
|
logger.info("Migrated preference: \(key)")
|
|
}
|
|
|
|
// Ensure changes are written to disk
|
|
UserDefaults.standard.synchronize()
|
|
|
|
// Try to remove old preferences file
|
|
try? FileManager.default.removeItem(at: oldPrefsURL)
|
|
logger.info("Removed old preferences file")
|
|
}
|
|
|
|
// Mark migration as complete
|
|
UserDefaults.standard.set(true, forKey: preferencesMigrationKey)
|
|
logger.info("Preferences migration completed successfully")
|
|
} else {
|
|
logger.info("No old preferences file found at: \(oldPrefsURL.path)")
|
|
// Mark migration as complete even if no old prefs found
|
|
UserDefaults.standard.set(true, forKey: preferencesMigrationKey)
|
|
}
|
|
} catch {
|
|
logger.error("Failed to migrate preferences: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
private func migrateSwiftDataStoreIfNeeded() {
|
|
// Check if migration has already been performed
|
|
if UserDefaults.standard.bool(forKey: swiftDataMigrationKey) {
|
|
logger.info("SwiftData migration already performed")
|
|
return
|
|
}
|
|
|
|
logger.info("Starting SwiftData store migration")
|
|
|
|
// Old location (in Containers directory)
|
|
let oldContainerURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
|
.deletingLastPathComponent() // Go up to Library
|
|
.appendingPathComponent("Containers/com.prakashjoshipax.VoiceInk/Data/Library/Application Support")
|
|
|
|
// New location (in Application Support)
|
|
let newContainerURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
|
.appendingPathComponent("com.prakashjoshipax.VoiceInk")
|
|
|
|
let storeFiles = [
|
|
"default.store",
|
|
"default.store-wal",
|
|
"default.store-shm"
|
|
]
|
|
|
|
do {
|
|
// Create new directory if it doesn't exist
|
|
try FileManager.default.createDirectory(at: newContainerURL, withIntermediateDirectories: true)
|
|
|
|
// Migrate each store file
|
|
for fileName in storeFiles {
|
|
let oldURL = oldContainerURL.appendingPathComponent(fileName)
|
|
let newURL = newContainerURL.appendingPathComponent(fileName)
|
|
|
|
if FileManager.default.fileExists(atPath: oldURL.path) {
|
|
logger.info("Migrating \(fileName)")
|
|
|
|
// If a file already exists at the destination, remove it first
|
|
if FileManager.default.fileExists(atPath: newURL.path) {
|
|
try FileManager.default.removeItem(at: newURL)
|
|
}
|
|
|
|
// Copy the file to new location
|
|
try FileManager.default.copyItem(at: oldURL, to: newURL)
|
|
|
|
// Remove the old file
|
|
try FileManager.default.removeItem(at: oldURL)
|
|
|
|
logger.info("Successfully migrated \(fileName)")
|
|
} else {
|
|
logger.info("No \(fileName) found at old location")
|
|
}
|
|
}
|
|
|
|
// Mark migration as complete
|
|
UserDefaults.standard.set(true, forKey: swiftDataMigrationKey)
|
|
logger.info("SwiftData migration completed successfully")
|
|
|
|
} catch {
|
|
logger.error("Failed to migrate SwiftData store: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
private func migrateWhisperModelsIfNeeded() {
|
|
// Check if migration has already been performed
|
|
if UserDefaults.standard.bool(forKey: whisperModelsMigrationKey) {
|
|
logger.info("Whisper models migration already performed")
|
|
return
|
|
}
|
|
|
|
logger.info("Starting Whisper models migration")
|
|
|
|
// Old location (in Containers directory)
|
|
let oldModelsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
.deletingLastPathComponent() // Go up to Documents
|
|
.deletingLastPathComponent() // Go up to Data
|
|
.deletingLastPathComponent() // Go up to com.prakashjoshipax.VoiceInk
|
|
.deletingLastPathComponent() // Go up to Containers
|
|
.appendingPathComponent("com.prakashjoshipax.VoiceInk/Data/Documents/WhisperModels")
|
|
|
|
// New location (in Documents)
|
|
let newModelsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
.appendingPathComponent("WhisperModels")
|
|
|
|
do {
|
|
// Create new directory if it doesn't exist
|
|
try FileManager.default.createDirectory(at: newModelsURL, withIntermediateDirectories: true)
|
|
|
|
// Get all files in the old directory
|
|
if let modelFiles = try? FileManager.default.contentsOfDirectory(at: oldModelsURL, includingPropertiesForKeys: nil) {
|
|
for modelURL in modelFiles {
|
|
let fileName = modelURL.lastPathComponent
|
|
let newURL = newModelsURL.appendingPathComponent(fileName)
|
|
|
|
logger.info("Migrating model: \(fileName)")
|
|
|
|
// If a file already exists at the destination, remove it first
|
|
if FileManager.default.fileExists(atPath: newURL.path) {
|
|
try FileManager.default.removeItem(at: newURL)
|
|
}
|
|
|
|
// Copy the file to new location
|
|
try FileManager.default.copyItem(at: modelURL, to: newURL)
|
|
|
|
// Remove the old file
|
|
try FileManager.default.removeItem(at: modelURL)
|
|
|
|
logger.info("Successfully migrated model: \(fileName)")
|
|
}
|
|
}
|
|
|
|
// Mark migration as complete
|
|
UserDefaults.standard.set(true, forKey: whisperModelsMigrationKey)
|
|
logger.info("Whisper models migration completed successfully")
|
|
|
|
} catch {
|
|
logger.error("Failed to migrate Whisper models: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
} |