ContactImageStorage.swift 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import CoreData
  2. import Foundation
  3. import SwiftUI
  4. import Swinject
  5. protocol ContactImageStorage {
  6. func fetchContactImageEntries() async -> [ContactImageEntry]
  7. func storeContactImageEntry(_ entry: ContactImageEntry) async
  8. func updateContactImageEntry(_ contactImageEntry: ContactImageEntry) async
  9. func deleteContactImageEntry(_ objectID: NSManagedObjectID) async
  10. }
  11. final class BaseContactImageStorage: ContactImageStorage, Injectable {
  12. @Injected() private var settingsManager: SettingsManager!
  13. private let backgroundContext = CoreDataStack.shared.newTaskContext()
  14. init(resolver: Resolver) {
  15. injectServices(resolver)
  16. }
  17. /// Fetches all stored Contact Trick entries.
  18. ///
  19. /// The method retrieves `ContactImageEntryStored` objects from Core Data, maps them to
  20. /// `ContactImageEntry` objects, and returns the results.
  21. ///
  22. /// - Returns: An array of `ContactImageEntry` objects.
  23. func fetchContactImageEntries() async -> [ContactImageEntry] {
  24. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  25. ofType: ContactImageEntryStored.self,
  26. onContext: backgroundContext,
  27. predicate: NSPredicate.all,
  28. key: "hasHighContrast",
  29. ascending: false
  30. )
  31. return await backgroundContext.perform {
  32. guard let fetchedContactImageEntries = results as? [ContactImageEntryStored] else { return [] }
  33. return fetchedContactImageEntries.compactMap { entry in
  34. ContactImageEntry(
  35. name: entry.name ?? "No name provided",
  36. layout: ContactImageLayout(rawValue: entry.layout ?? "Default") ?? .default,
  37. ring: ContactImageLargeRing(rawValue: entry.ring ?? "Hidden") ?? .none,
  38. primary: ContactImageValue(rawValue: entry.primary ?? "Glucose Reading") ?? .glucose,
  39. top: ContactImageValue(rawValue: entry.top ?? "None") ?? .none,
  40. bottom: ContactImageValue(rawValue: entry.bottom ?? "None") ?? .none,
  41. contactId: entry.contactId?.string,
  42. hasHighContrast: entry.hasHighContrast,
  43. ringWidth: ContactImageEntry.RingWidth(rawValue: Int(entry.ringWidth)) ?? .regular,
  44. ringGap: ContactImageEntry.RingGap(rawValue: Int(entry.ringGap)) ?? .small,
  45. fontSize: ContactImageEntry.FontSize(rawValue: Int(entry.fontSize)) ?? .regular,
  46. secondaryFontSize: ContactImageEntry.FontSize(rawValue: Int(entry.fontSizeSecondary)) ?? .small,
  47. fontWeight: Font.Weight.fromString(entry.fontWeight ?? "regular"),
  48. fontWidth: Font.Width.fromString(entry.fontWidth ?? "standard"),
  49. managedObjectID: entry.objectID
  50. )
  51. }
  52. }
  53. }
  54. /// Stores a new Contact Trick entry.
  55. ///
  56. /// This method creates a new `ContactImageEntryStored` object in the background context,
  57. /// populates its properties with the values from the provided `ContactImageEntry`, and
  58. /// saves the context if changes exist.
  59. ///
  60. /// - Parameter contactImageEntry: The `ContactImageEntry` object to be stored.
  61. func storeContactImageEntry(_ contactImageEntry: ContactImageEntry) async {
  62. await backgroundContext.perform {
  63. let newContactImageEntry = ContactImageEntryStored(context: self.backgroundContext)
  64. newContactImageEntry.id = UUID()
  65. newContactImageEntry.name = contactImageEntry.name
  66. newContactImageEntry.contactId = contactImageEntry.contactId
  67. newContactImageEntry.layout = contactImageEntry.layout.rawValue
  68. newContactImageEntry.ring = contactImageEntry.ring.rawValue
  69. newContactImageEntry.primary = contactImageEntry.primary.rawValue
  70. newContactImageEntry.top = contactImageEntry.top.rawValue
  71. newContactImageEntry.bottom = contactImageEntry.bottom.rawValue
  72. newContactImageEntry.hasHighContrast = contactImageEntry.hasHighContrast
  73. newContactImageEntry.ringWidth = Int16(contactImageEntry.ringWidth.rawValue)
  74. newContactImageEntry.ringGap = Int16(contactImageEntry.ringGap.rawValue)
  75. newContactImageEntry.fontSize = Int16(contactImageEntry.fontSize.rawValue)
  76. newContactImageEntry.fontSizeSecondary = Int16(contactImageEntry.secondaryFontSize.rawValue)
  77. newContactImageEntry.fontWidth = contactImageEntry.fontWeight.asString
  78. newContactImageEntry.fontWeight = contactImageEntry.fontWidth.asString
  79. do {
  80. guard self.backgroundContext.hasChanges else { return }
  81. try self.backgroundContext.save()
  82. } catch let error as NSError {
  83. debugPrint(
  84. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to save Contact Trick Entry to Core Data with error: \(error.userInfo)"
  85. )
  86. }
  87. }
  88. }
  89. /// Updates an existing Contact Trick entry in Core Data.
  90. ///
  91. /// This method finds the existing `ContactImageEntryStored` object by its `contactId` and updates
  92. /// its properties with the values from the provided `ContactImageEntry`. If no matching entry exists,
  93. /// it does nothing.
  94. ///
  95. /// - Parameter contactImageEntry: The `ContactImageEntry` object with updated values.
  96. func updateContactImageEntry(_ contactImageEntry: ContactImageEntry) async {
  97. await backgroundContext.perform {
  98. let fetchRequest: NSFetchRequest<ContactImageEntryStored> = ContactImageEntryStored.fetchRequest()
  99. fetchRequest.predicate = NSPredicate(format: "contactId == %@", contactImageEntry.contactId ?? "")
  100. do {
  101. if let existingEntry = try self.backgroundContext.fetch(fetchRequest).first {
  102. // Update the properties of the existing entry
  103. existingEntry.name = contactImageEntry.name
  104. existingEntry.layout = contactImageEntry.layout.rawValue
  105. existingEntry.ring = contactImageEntry.ring.rawValue
  106. existingEntry.primary = contactImageEntry.primary.rawValue
  107. existingEntry.top = contactImageEntry.top.rawValue
  108. existingEntry.bottom = contactImageEntry.bottom.rawValue
  109. existingEntry.hasHighContrast = contactImageEntry.hasHighContrast
  110. existingEntry.ringWidth = Int16(contactImageEntry.ringWidth.rawValue)
  111. existingEntry.ringGap = Int16(contactImageEntry.ringGap.rawValue)
  112. existingEntry.fontSize = Int16(contactImageEntry.fontSize.rawValue)
  113. existingEntry.fontSizeSecondary = Int16(contactImageEntry.secondaryFontSize.rawValue)
  114. existingEntry.fontWeight = contactImageEntry.fontWeight.asString
  115. existingEntry.fontWidth = contactImageEntry.fontWidth.asString
  116. guard self.backgroundContext.hasChanges else { return }
  117. try self.backgroundContext.save()
  118. } else {
  119. debugPrint(
  120. "\(DebuggingIdentifiers.failed) \(#file) \(#function) No matching Contact Trick Entry found to update."
  121. )
  122. }
  123. } catch let error as NSError {
  124. debugPrint(
  125. "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to update Contact Trick Entry with error: \(error.userInfo)"
  126. )
  127. }
  128. }
  129. }
  130. /// Deletes a Contact Trick entry from Core Data.
  131. ///
  132. /// - Parameter objectID: The `NSManagedObjectID` of the object to delete.
  133. func deleteContactImageEntry(_ objectID: NSManagedObjectID) async {
  134. await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
  135. }
  136. }