You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 lines
9.7 KiB

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(String(format: "core_data_load_failed".localized, error.localizedDescription))
//
if let nsError = error as NSError?,
nsError.domain == NSCocoaErrorDomain && (nsError.code == 134030 || nsError.code == 134140) {
print("architecture_mismatch_detected".localized)
self.deleteDatabaseFiles()
//
self.container.loadPersistentStores { _, reloadError in
if let reloadError = reloadError {
print(String(format: "core_data_reload_failed".localized, reloadError.localizedDescription))
} else {
print("core_data_reload_success".localized)
}
}
}
}
}
//
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}
//
func save() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
print("core_data_save_success".localized)
} catch {
print(String(format: "core_data_save_failed".localized, error.localizedDescription))
print(String(format: "error_details".localized, "\(error)"))
// NSError
if let nsError = error as NSError? {
print(String(format: "error_domain".localized, nsError.domain))
print("❌ Error code: \(nsError.code)")
print("❌ User info: \(nsError.userInfo)")
// Transformable
if nsError.domain == NSCocoaErrorDomain && nsError.code == 134030 {
print("❌ May be Transformable property encoding error")
}
}
}
} else {
print(" No changes to save")
}
}
//
private let pageSize = 20
//
func fetchHistoryItems(page: Int = 0) -> [HistoryItem] {
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)]
request.fetchLimit = pageSize
request.fetchOffset = page * pageSize
do {
return try container.viewContext.fetch(request)
} catch {
print("Failed to fetch history: \(error.localizedDescription)")
return []
}
}
//
func fetchAllHistoryItems() -> [HistoryItem] {
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \HistoryItem.createdAt, ascending: false)]
do {
return try container.viewContext.fetch(request)
} catch {
print("Failed to fetch all history: \(error.localizedDescription)")
return []
}
}
//
func fetchHistoryItemsCount() -> Int {
let request: NSFetchRequest<HistoryItem> = HistoryItem.fetchRequest()
do {
return try container.viewContext.count(for: request)
} catch {
print("Failed to count history: \(error.localizedDescription)")
return 0
}
}
//
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("Failed to clear history: \(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 []
}
}
}