ContactTrickStorage.swift 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import CoreData
  2. import Foundation
  3. import SwiftUI
  4. import Swinject
  5. protocol ContactTrickStorage {
  6. func fetchContactTrickEntries() async -> [ContactTrickEntry]
  7. func storeContactTrickEntry(_ entry: ContactTrickEntry) async
  8. func updateContactTrickEntry(_ contactTrickEntry: ContactTrickEntry) async
  9. func deleteContactTrickEntry(_ objectID: NSManagedObjectID) async
  10. }
  11. final class BaseContactTrickStorage: ContactTrickStorage, 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 `ContactTrickEntryStored` objects from Core Data, maps them to
  20. /// `ContactTrickEntry` objects, and returns the results.
  21. ///
  22. /// - Returns: An array of `ContactTrickEntry` objects.
  23. func fetchContactTrickEntries() async -> [ContactTrickEntry] {
  24. let results = await CoreDataStack.shared.fetchEntitiesAsync(
  25. ofType: ContactTrickEntryStored.self,
  26. onContext: backgroundContext,
  27. predicate: NSPredicate.all,
  28. key: "isDarkMode",
  29. ascending: false
  30. )
  31. return await backgroundContext.perform {
  32. guard let fetchedContactTrickEntries = results as? [ContactTrickEntryStored] else { return [] }
  33. return fetchedContactTrickEntries.compactMap { entry in
  34. ContactTrickEntry(
  35. name: entry.name ?? "No name provided",
  36. layout: ContactTrickLayout(rawValue: entry.layout ?? "Single") ?? .single,
  37. ring: ContactTrickLargeRing(rawValue: entry.ring ?? "DontShowRing") ?? .none,
  38. primary: ContactTrickValue(rawValue: entry.primary ?? "GlucoseContactValue") ?? .glucose,
  39. top: ContactTrickValue(rawValue: entry.top ?? "NoneContactValue") ?? .none,
  40. bottom: ContactTrickValue(rawValue: entry.bottom ?? "NoneContactValue") ?? .none,
  41. contactId: entry.contactId?.string,
  42. darkMode: entry.isDarkMode,
  43. ringWidth: ContactTrickEntry.RingWidth(rawValue: Int(entry.ringWidth)) ?? .regular,
  44. ringGap: ContactTrickEntry.RingGap(rawValue: Int(entry.ringGap)) ?? .small,
  45. fontSize: ContactTrickEntry.FontSize(rawValue: Int(entry.fontSize)) ?? .regular,
  46. secondaryFontSize: ContactTrickEntry.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 `ContactTrickEntryStored` object in the background context,
  57. /// populates its properties with the values from the provided `ContactTrickEntry`, and
  58. /// saves the context if changes exist.
  59. ///
  60. /// - Parameter contactTrickEntry: The `ContactTrickEntry` object to be stored.
  61. func storeContactTrickEntry(_ contactTrickEntry: ContactTrickEntry) async {
  62. await backgroundContext.perform {
  63. let newContactTrickEntry = ContactTrickEntryStored(context: self.backgroundContext)
  64. newContactTrickEntry.id = UUID()
  65. newContactTrickEntry.name = contactTrickEntry.name
  66. newContactTrickEntry.contactId = contactTrickEntry.contactId
  67. newContactTrickEntry.layout = contactTrickEntry.layout.rawValue
  68. newContactTrickEntry.ring = contactTrickEntry.ring.rawValue
  69. newContactTrickEntry.primary = contactTrickEntry.primary.rawValue
  70. newContactTrickEntry.top = contactTrickEntry.top.rawValue
  71. newContactTrickEntry.bottom = contactTrickEntry.bottom.rawValue
  72. newContactTrickEntry.isDarkMode = contactTrickEntry.darkMode
  73. newContactTrickEntry.ringWidth = Int16(contactTrickEntry.ringWidth.rawValue)
  74. newContactTrickEntry.ringGap = Int16(contactTrickEntry.ringGap.rawValue)
  75. newContactTrickEntry.fontSize = Int16(contactTrickEntry.fontSize.rawValue)
  76. newContactTrickEntry.fontSizeSecondary = Int16(contactTrickEntry.secondaryFontSize.rawValue)
  77. newContactTrickEntry.fontWidth = contactTrickEntry.fontWeight.asString
  78. newContactTrickEntry.fontWeight = contactTrickEntry.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 `ContactTrickEntryStored` object by its `contactId` and updates
  92. /// its properties with the values from the provided `ContactTrickEntry`. If no matching entry exists,
  93. /// it does nothing.
  94. ///
  95. /// - Parameter contactTrickEntry: The `ContactTrickEntry` object with updated values.
  96. func updateContactTrickEntry(_ contactTrickEntry: ContactTrickEntry) async {
  97. await backgroundContext.perform {
  98. let fetchRequest: NSFetchRequest<ContactTrickEntryStored> = ContactTrickEntryStored.fetchRequest()
  99. fetchRequest.predicate = NSPredicate(format: "contactId == %@", contactTrickEntry.contactId ?? "")
  100. do {
  101. if let existingEntry = try self.backgroundContext.fetch(fetchRequest).first {
  102. // Update the properties of the existing entry
  103. existingEntry.name = contactTrickEntry.name
  104. existingEntry.layout = contactTrickEntry.layout.rawValue
  105. existingEntry.ring = contactTrickEntry.ring.rawValue
  106. existingEntry.primary = contactTrickEntry.primary.rawValue
  107. existingEntry.top = contactTrickEntry.top.rawValue
  108. existingEntry.bottom = contactTrickEntry.bottom.rawValue
  109. existingEntry.isDarkMode = contactTrickEntry.darkMode
  110. existingEntry.ringWidth = Int16(contactTrickEntry.ringWidth.rawValue)
  111. existingEntry.ringGap = Int16(contactTrickEntry.ringGap.rawValue)
  112. existingEntry.fontSize = Int16(contactTrickEntry.fontSize.rawValue)
  113. existingEntry.fontSizeSecondary = Int16(contactTrickEntry.secondaryFontSize.rawValue)
  114. existingEntry.fontWeight = contactTrickEntry.fontWeight.asString
  115. existingEntry.fontWidth = contactTrickEntry.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 deleteContactTrickEntry(_ objectID: NSManagedObjectID) async {
  134. await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
  135. }
  136. }