|
|
import Foundation
|
|
|
import CoreData
|
|
|
import SwiftUI
|
|
|
import Combine
|
|
|
|
|
|
class CoreDataManager: ObservableObject {
|
|
|
static let shared = CoreDataManager()
|
|
|
|
|
|
let container: NSPersistentContainer
|
|
|
|
|
|
init() {
|
|
|
container = NSPersistentContainer(name: "MyQrCode")
|
|
|
|
|
|
container.loadPersistentStores { description, error in
|
|
|
if let error = error {
|
|
|
print("Core Data 加载失败: \(error.localizedDescription)")
|
|
|
|
|
|
// 如果是架构不匹配错误,删除数据库文件并重新创建
|
|
|
if let nsError = error as NSError?,
|
|
|
nsError.domain == NSCocoaErrorDomain && (nsError.code == 134030 || nsError.code == 134140) {
|
|
|
print("🔄 检测到架构不匹配,删除现有数据库文件")
|
|
|
self.deleteDatabaseFiles()
|
|
|
|
|
|
// 重新加载持久化存储
|
|
|
self.container.loadPersistentStores { _, reloadError in
|
|
|
if let reloadError = reloadError {
|
|
|
print("❌ 重新加载Core Data失败: \(reloadError.localizedDescription)")
|
|
|
} else {
|
|
|
print("✅ Core Data重新加载成功")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 启用自动合并更改
|
|
|
container.viewContext.automaticallyMergesChangesFromParent = true
|
|
|
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
|
|
}
|
|
|
|
|
|
// 保存上下文
|
|
|
func save() {
|
|
|
let context = container.viewContext
|
|
|
|
|
|
if context.hasChanges {
|
|
|
do {
|
|
|
try context.save()
|
|
|
print("✅ Core Data保存成功")
|
|
|
} catch {
|
|
|
print("❌ Core Data保存失败: \(error.localizedDescription)")
|
|
|
print("❌ 错误详情: \(error)")
|
|
|
|
|
|
// 如果是NSError,打印更多信息
|
|
|
if let nsError = error as NSError? {
|
|
|
print("❌ 错误域: \(nsError.domain)")
|
|
|
print("❌ 错误代码: \(nsError.code)")
|
|
|
print("❌ 用户信息: \(nsError.userInfo)")
|
|
|
|
|
|
// 检查是否是Transformable属性错误
|
|
|
if nsError.domain == NSCocoaErrorDomain && nsError.code == 134030 {
|
|
|
print("❌ 可能是Transformable属性编码错误")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
print("ℹ️ 没有更改需要保存")
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 获取历史记录
|
|
|
func fetchHistoryItems() -> [HistoryItem] {
|
|
|
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
|
|
|
request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)]
|
|
|
|
|
|
do {
|
|
|
return try container.viewContext.fetch(request)
|
|
|
} catch {
|
|
|
print("获取历史记录失败: \(error.localizedDescription)")
|
|
|
return []
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 添加历史记录
|
|
|
func addHistoryItem(_ item: HistoryItem) {
|
|
|
container.viewContext.insert(item)
|
|
|
save()
|
|
|
}
|
|
|
|
|
|
// 删除历史记录
|
|
|
func deleteHistoryItem(_ item: HistoryItem) {
|
|
|
container.viewContext.delete(item)
|
|
|
save()
|
|
|
}
|
|
|
|
|
|
// 清空所有历史记录
|
|
|
func clearAllHistory() {
|
|
|
let request: NSFetchRequest<NSFetchRequestResult> = HistoryItem.fetchRequest()
|
|
|
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
|
|
|
|
|
|
do {
|
|
|
try container.viewContext.execute(deleteRequest)
|
|
|
save()
|
|
|
} catch {
|
|
|
print("清空历史记录失败: \(error.localizedDescription)")
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 删除数据库文件
|
|
|
private func deleteDatabaseFiles() {
|
|
|
let fileManager = FileManager.default
|
|
|
|
|
|
// 获取应用支持目录
|
|
|
guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else {
|
|
|
print("❌ 无法获取应用支持目录")
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 删除MyQrCode.sqlite及其相关文件
|
|
|
let databaseName = "MyQrCode"
|
|
|
let possibleFiles = [
|
|
|
appSupportURL.appendingPathComponent("\(databaseName).sqlite"),
|
|
|
appSupportURL.appendingPathComponent("\(databaseName).sqlite-shm"),
|
|
|
appSupportURL.appendingPathComponent("\(databaseName).sqlite-wal")
|
|
|
]
|
|
|
|
|
|
for fileURL in possibleFiles {
|
|
|
if fileManager.fileExists(atPath: fileURL.path) {
|
|
|
do {
|
|
|
try fileManager.removeItem(at: fileURL)
|
|
|
print("✅ 删除数据库文件: \(fileURL.lastPathComponent)")
|
|
|
} catch {
|
|
|
print("❌ 删除数据库文件失败: \(error)")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 也检查文档目录
|
|
|
guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
let documentFiles = [
|
|
|
documentsURL.appendingPathComponent("\(databaseName).sqlite"),
|
|
|
documentsURL.appendingPathComponent("\(databaseName).sqlite-shm"),
|
|
|
documentsURL.appendingPathComponent("\(databaseName).sqlite-wal")
|
|
|
]
|
|
|
|
|
|
for fileURL in documentFiles {
|
|
|
if fileManager.fileExists(atPath: fileURL.path) {
|
|
|
do {
|
|
|
try fileManager.removeItem(at: fileURL)
|
|
|
print("✅ 删除数据库文件: \(fileURL.lastPathComponent)")
|
|
|
} catch {
|
|
|
print("❌ 删除数据库文件失败: \(error)")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 搜索历史记录
|
|
|
func searchHistoryItems(query: String) -> [HistoryItem] {
|
|
|
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
|
|
|
|
|
|
if !query.isEmpty {
|
|
|
let contentPredicate = NSPredicate(format: "content CONTAINS[cd] %@", query)
|
|
|
let barcodeTypePredicate = NSPredicate(format: "barcodeType CONTAINS[cd] %@", query)
|
|
|
let qrCodeTypePredicate = NSPredicate(format: "qrCodeType CONTAINS[cd] %@", query)
|
|
|
|
|
|
let compoundPredicate = NSCompoundPredicate(
|
|
|
orPredicateWithSubpredicates: [
|
|
|
contentPredicate,
|
|
|
barcodeTypePredicate,
|
|
|
qrCodeTypePredicate
|
|
|
]
|
|
|
)
|
|
|
|
|
|
request.predicate = compoundPredicate
|
|
|
}
|
|
|
|
|
|
request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)]
|
|
|
|
|
|
do {
|
|
|
return try container.viewContext.fetch(request)
|
|
|
} catch {
|
|
|
print("搜索历史记录失败: \(error.localizedDescription)")
|
|
|
return []
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 按类型过滤
|
|
|
func filterByType(_ type: DataType) -> [HistoryItem] {
|
|
|
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
|
|
|
request.predicate = NSPredicate(format: "dataType == %@", type.rawValue)
|
|
|
request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)]
|
|
|
|
|
|
do {
|
|
|
return try container.viewContext.fetch(request)
|
|
|
} catch {
|
|
|
print("按类型过滤失败: \(error.localizedDescription)")
|
|
|
return []
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 按来源过滤
|
|
|
func filterBySource(_ source: DataSource) -> [HistoryItem] {
|
|
|
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
|
|
|
request.predicate = NSPredicate(format: "dataSource == %@", source.rawValue)
|
|
|
request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)]
|
|
|
|
|
|
do {
|
|
|
return try container.viewContext.fetch(request)
|
|
|
} catch {
|
|
|
print("按来源过滤失败: \(error.localizedDescription)")
|
|
|
return []
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 获取收藏项目
|
|
|
func getFavoriteItems() -> [HistoryItem] {
|
|
|
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
|
|
|
request.predicate = NSPredicate(format: "isFavorite == YES")
|
|
|
request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)]
|
|
|
|
|
|
do {
|
|
|
return try container.viewContext.fetch(request)
|
|
|
} catch {
|
|
|
print("获取收藏项目失败: \(error.localizedDescription)")
|
|
|
return []
|
|
|
}
|
|
|
}
|
|
|
} |