MigrationScript.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import CoreData
  2. import Foundation
  3. // MARK: - Protocol Definition
  4. /// A protocol that ensures a Data Transfer Object (DTO) can be stored in Core Data.
  5. /// It requires a method to map the DTO to its corresponding Core Data managed object.
  6. protocol ImportableDTO: Decodable {
  7. associatedtype ManagedObject: NSManagedObject
  8. /// Converts the DTO into a Core Data managed object.
  9. func store(in context: NSManagedObjectContext) -> ManagedObject
  10. }
  11. // MARK: - JSONImporter Class with Generic Import Function
  12. /// Class responsible for importing JSON data into Core Data.
  13. class JSONImporter {
  14. private let context: NSManagedObjectContext
  15. private let fileManager = FileManager.default
  16. /// Initializes the importer with a Core Data context.
  17. init(context: NSManagedObjectContext) {
  18. self.context = context
  19. }
  20. /// Generic function to import data from a JSON file into Core Data.
  21. /// - Parameters:
  22. /// - userDefaultsKey: Key to check if data has already been imported.
  23. /// - filePathComponent: Path component of the JSON file.
  24. /// - dtoType: The DTO type conforming to `ImportableDTO`.
  25. /// - dateDecodingStrategy: The date decoding strategy for JSON decoding.
  26. func importDataIfNeeded<T: ImportableDTO>(
  27. userDefaultsKey: String,
  28. filePathComponent: String,
  29. dtoType _: T.Type,
  30. dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .iso8601
  31. ) async {
  32. let hasImported = UserDefaults.standard.bool(forKey: userDefaultsKey)
  33. guard !hasImported else {
  34. debugPrint("\(filePathComponent) already imported. Skipping import.")
  35. return
  36. }
  37. do {
  38. // Get the file path for the JSON file
  39. guard let filePath = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
  40. .first?
  41. .appendingPathComponent(filePathComponent),
  42. fileManager.fileExists(atPath: filePath.path)
  43. else {
  44. debugPrint("\(filePathComponent) file not found at path \(filePathComponent)")
  45. return
  46. }
  47. // Read data from the JSON file
  48. let data = try Data(contentsOf: filePath)
  49. let decoder = JSONDecoder()
  50. decoder.dateDecodingStrategy = dateDecodingStrategy
  51. // Decode the data into an array of DTOs
  52. let entries = try decoder.decode([T].self, from: data)
  53. // Save the DTOs into Core Data
  54. await context.perform {
  55. for entry in entries {
  56. _ = entry.store(in: self.context)
  57. }
  58. do {
  59. guard self.context.hasChanges else { return }
  60. try self.context.save()
  61. debugPrint("\(DebuggingIdentifiers.succeeded) \(filePathComponent) successfully imported into Core Data.")
  62. } catch {
  63. debugPrint("\(DebuggingIdentifiers.failed) Failed to save \(filePathComponent) to Core Data: \(error)")
  64. }
  65. }
  66. // Delete the JSON file after successful import
  67. try fileManager.removeItem(at: filePath)
  68. debugPrint("\(filePathComponent) deleted after successful import.")
  69. // Update UserDefaults to indicate that the data has been imported
  70. UserDefaults.standard.set(true, forKey: userDefaultsKey)
  71. } catch {
  72. debugPrint("Error importing \(filePathComponent): \(error)")
  73. }
  74. }
  75. }
  76. // MARK: - Extension for Specific Import Functions
  77. extension JSONImporter {
  78. func importPumpHistoryIfNeeded() async {
  79. await importDataIfNeeded(
  80. userDefaultsKey: "pumpHistoryImported",
  81. filePathComponent: OpenAPS.Monitor.pumpHistory,
  82. dtoType: PumpEventDTO.self,
  83. dateDecodingStrategy: .iso8601
  84. )
  85. }
  86. func importCarbHistoryIfNeeded() async {
  87. await importDataIfNeeded(
  88. userDefaultsKey: "carbHistoryImported",
  89. filePathComponent: OpenAPS.Monitor.carbHistory,
  90. dtoType: CarbEntryDTO.self,
  91. dateDecodingStrategy: .iso8601
  92. )
  93. }
  94. }