CoreDataStack.swift 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import CoreData
  2. import Foundation
  3. class CoreDataStack: ObservableObject {
  4. init() {}
  5. static let shared = CoreDataStack()
  6. static let identifier = "CoreDataStack"
  7. lazy var persistentContainer: NSPersistentContainer = {
  8. let container = NSPersistentContainer(name: "Core_Data")
  9. container.loadPersistentStores(completionHandler: { _, error in
  10. guard let error = error as NSError? else { return }
  11. fatalError("Unresolved error: \(error), \(error.userInfo)")
  12. })
  13. return container
  14. }()
  15. // ensure thread safety by creating a NSManagedObjectContext for the main thread and for a background thread
  16. lazy var backgroundContext: NSManagedObjectContext = {
  17. let newbackgroundContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
  18. newbackgroundContext.automaticallyMergesChangesFromParent = true
  19. newbackgroundContext
  20. .mergePolicy =
  21. NSMergeByPropertyStoreTrumpMergePolicy // if two objects with the same unique constraint are found, overwrite with the object in the external storage
  22. return newbackgroundContext
  23. }()
  24. lazy var viewContext: NSManagedObjectContext = {
  25. let viewContext = CoreDataStack.shared.persistentContainer.viewContext
  26. viewContext.automaticallyMergesChangesFromParent = true
  27. return viewContext
  28. }()
  29. func fetchEntities<T: NSManagedObject>(
  30. ofType type: T.Type,
  31. predicate: NSPredicate,
  32. key: String,
  33. ascending: Bool,
  34. fetchLimit: Int? = nil,
  35. batchSize: Int? = nil,
  36. propertiesToFetch: [String]? = nil,
  37. callingFunction: String = #function,
  38. callingClass: String = #fileID
  39. ) -> [T] {
  40. let request = NSFetchRequest<T>(entityName: String(describing: type))
  41. request.sortDescriptors = [NSSortDescriptor(key: key, ascending: ascending)]
  42. request.predicate = predicate
  43. if let limit = fetchLimit {
  44. request.fetchLimit = limit
  45. }
  46. if let batchSize = batchSize {
  47. request.fetchBatchSize = batchSize
  48. }
  49. if let propertiesTofetch = propertiesToFetch {
  50. request.propertiesToFetch = propertiesTofetch
  51. request.resultType = .dictionaryResultType
  52. } else {
  53. request.resultType = .managedObjectResultType
  54. }
  55. var result: [T]?
  56. /// we need to ensure that the fetch immediately returns a value as long as the whole app does not use the async await pattern, otherwise we could perform this asynchronously with backgroundContext.perform and not block the thread
  57. backgroundContext.performAndWait {
  58. do {
  59. debugPrint("Fetching \(T.self) in \(callingFunction) from \(callingClass): \(DebuggingIdentifiers.succeeded)")
  60. result = try self.backgroundContext.fetch(request)
  61. } catch {
  62. debugPrint(
  63. "Fetching \(T.self) in \(callingFunction) from \(callingClass): \(DebuggingIdentifiers.failed) \(error)"
  64. )
  65. }
  66. }
  67. return result ?? []
  68. }
  69. }