GlucoseTargetSetup.swift 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import Foundation
  2. extension Home.StateModel {
  3. /**
  4. Processes raw glucose target data into a list of target profiles for visualization.
  5. - Parameters:
  6. - rawTargets: The raw glucose target data containing offset and glucose values.
  7. - startMarker: The reference date to start the target profiles from.
  8. - Returns: An array of `TargetProfile` objects, each representing a glucose target range starting from the day of the startMarker and ending two days later.
  9. The function:
  10. - Converts glucose targets into profiles covering three consecutive days (day of startMarker, day after startMarker and day after that).
  11. - Calculates start and end times for each target based on the offsets provided.
  12. - Handles conversions between mg/dL and mmol/L as per user settings.
  13. - Ensures targets span across midnight to avoid data cutoff.
  14. */
  15. func processFetchedTargets(_ rawTargets: BGTargets, startMarker: Date) -> [TargetProfile] {
  16. var targetProfiles: [TargetProfile] = []
  17. // Ensure there are targets to process
  18. guard !rawTargets.targets.isEmpty else {
  19. debugPrint("\(DebuggingIdentifiers.failed) Warning: No targets to process in rawTargets.")
  20. return []
  21. }
  22. let targets = rawTargets.targets
  23. // Base date is the start of the day for the startMarker
  24. let baseDate = Calendar.current.startOfDay(for: startMarker)
  25. // Process each target three times
  26. for index in 0 ..< (targets.count * 3) {
  27. // Calculate the day offset (0 for today, 1 for tomorrow, 2 for day after)
  28. let dayOffset = index / targets.count
  29. let targetIndex = index % targets.count
  30. // Validate target index to ensure safety
  31. guard targetIndex < targets.count else {
  32. debugPrint("\(DebuggingIdentifiers.failed) Error: Invalid target index \(targetIndex).")
  33. continue
  34. }
  35. // Fetch the target for the current iteration
  36. let target = targets[targetIndex]
  37. // Calculate the time offset for the current day
  38. let dayTimeOffset = TimeInterval(dayOffset * 24 * 60 * 60)
  39. // Calculate the start time for the current target
  40. let startTime = baseDate
  41. .addingTimeInterval(dayTimeOffset)
  42. .addingTimeInterval(TimeInterval(target.offset * 60))
  43. // Calculate the end time for the current target
  44. let endTime: Date = {
  45. if targetIndex + 1 < targets.count {
  46. // End time is the start time of the next target within the same day
  47. return baseDate
  48. .addingTimeInterval(dayTimeOffset)
  49. .addingTimeInterval(TimeInterval(targets[targetIndex + 1].offset * 60))
  50. } else {
  51. // End time is the end of the day (midnight of the next day)
  52. return baseDate.addingTimeInterval(dayTimeOffset + 24 * 60 * 60)
  53. }
  54. }()
  55. // Convert glucose value based on user unit preference (mg/dL or mmol/L)
  56. let targetValue = units == .mgdL ? target.low : target.low.asMmolL
  57. // Append the processed target profile to the list
  58. targetProfiles.append(
  59. TargetProfile(
  60. value: targetValue,
  61. startTime: startTime.timeIntervalSinceReferenceDate,
  62. endTime: endTime.timeIntervalSinceReferenceDate
  63. )
  64. )
  65. }
  66. return targetProfiles
  67. }
  68. }
  69. struct TargetProfile: Hashable {
  70. let value: Decimal
  71. let startTime: TimeInterval
  72. let endTime: TimeInterval
  73. }
  74. private extension Date {
  75. var startOfDay: Date {
  76. Calendar.current.startOfDay(for: self)
  77. }
  78. }