From cc5f475640a456dc7fc982b7bec0305024c96ac2 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Fri, 18 Apr 2025 14:34:05 -0500 Subject: [PATCH 1/2] Editing custom presets --- LoopKit.xcodeproj/project.pbxproj | 24 +-- LoopKit/DailyQuantitySchedule+Override.swift | 2 +- LoopKit/SettingsStore.swift | 6 +- LoopKit/TemporaryPreset.swift | 137 ++++++++++++++++++ ...gs.swift => TemporaryPresetSettings.swift} | 6 +- LoopKit/TemporaryScheduleOverride.swift | 16 +- .../TemporaryScheduleOverrideHistory.swift | 2 +- LoopKit/TemporaryScheduleOverridePreset.swift | 70 --------- LoopKit/TherapySettings.swift | 4 +- LoopKitTests/DosingDecisionStoreTests.swift | 2 +- LoopKitTests/SettingsStoreTests.swift | 4 +- ...emporaryScheduleOverrideHistoryTests.swift | 2 +- .../TemporaryScheduleOverrideTests.swift | 12 +- .../AddEditOverrideTableViewController.swift | 20 +-- .../OverrideSelectionViewController.swift | 12 +- .../ViewModels/TherapySettingsViewModel.swift | 2 +- 16 files changed, 194 insertions(+), 127 deletions(-) create mode 100644 LoopKit/TemporaryPreset.swift rename LoopKit/{TemporaryScheduleOverrideSettings.swift => TemporaryPresetSettings.swift} (93%) delete mode 100644 LoopKit/TemporaryScheduleOverridePreset.swift diff --git a/LoopKit.xcodeproj/project.pbxproj b/LoopKit.xcodeproj/project.pbxproj index 1c036c44b..b85a9bf0b 100644 --- a/LoopKit.xcodeproj/project.pbxproj +++ b/LoopKit.xcodeproj/project.pbxproj @@ -373,9 +373,9 @@ 89ADE12F226BDED40067222B /* DateRelativeBolusEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89ADE12E226BDED40067222B /* DateRelativeBolusEntry.swift */; }; 89ADE134226BF0490067222B /* TestingScenarioInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89ADE133226BF0490067222B /* TestingScenarioInstance.swift */; }; 89ADE136226BF0BE0067222B /* DateRelativeGlucoseSample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89ADE135226BF0BE0067222B /* DateRelativeGlucoseSample.swift */; }; - 89AE2226228BC54C00BDFD85 /* TemporaryScheduleOverridePreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE221F228BC54C00BDFD85 /* TemporaryScheduleOverridePreset.swift */; }; + 89AE2226228BC54C00BDFD85 /* TemporaryPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE221F228BC54C00BDFD85 /* TemporaryPreset.swift */; }; 89AE2227228BC54C00BDFD85 /* TemporaryScheduleOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2223228BC54C00BDFD85 /* TemporaryScheduleOverride.swift */; }; - 89AE2228228BC54C00BDFD85 /* TemporaryScheduleOverrideSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2224228BC54C00BDFD85 /* TemporaryScheduleOverrideSettings.swift */; }; + 89AE2228228BC54C00BDFD85 /* TemporaryPresetSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2224228BC54C00BDFD85 /* TemporaryPresetSettings.swift */; }; 89AE2229228BC54C00BDFD85 /* TemporaryScheduleOverrideHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2225228BC54C00BDFD85 /* TemporaryScheduleOverrideHistory.swift */; }; 89AE222B228BC56A00BDFD85 /* WeakSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE222A228BC56A00BDFD85 /* WeakSet.swift */; }; 89AE222C228BC66E00BDFD85 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AC792E224C781200B8E9BA /* Locked.swift */; }; @@ -391,8 +391,8 @@ 89AE2237228BCAAB00BDFD85 /* EGPSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 891A3FD8224BEB4500378B27 /* EGPSchedule.swift */; }; 89AE2238228BCAAB00BDFD85 /* TemporaryScheduleOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2223228BC54C00BDFD85 /* TemporaryScheduleOverride.swift */; }; 89AE2239228BCAAB00BDFD85 /* TemporaryScheduleOverrideHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2225228BC54C00BDFD85 /* TemporaryScheduleOverrideHistory.swift */; }; - 89AE223A228BCAAB00BDFD85 /* TemporaryScheduleOverridePreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE221F228BC54C00BDFD85 /* TemporaryScheduleOverridePreset.swift */; }; - 89AE223B228BCAAB00BDFD85 /* TemporaryScheduleOverrideSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2224228BC54C00BDFD85 /* TemporaryScheduleOverrideSettings.swift */; }; + 89AE223A228BCAAB00BDFD85 /* TemporaryPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE221F228BC54C00BDFD85 /* TemporaryPreset.swift */; }; + 89AE223B228BCAAB00BDFD85 /* TemporaryPresetSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE2224228BC54C00BDFD85 /* TemporaryPresetSettings.swift */; }; 89AE223C228BCAAB00BDFD85 /* WeakSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AE222A228BC56A00BDFD85 /* WeakSet.swift */; }; 89AE223E228BD3C400BDFD85 /* SetConstrainedScheduleEntryTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 892A5D62222F6B13008961AB /* SetConstrainedScheduleEntryTableViewCell.xib */; }; 89AF78C02447E285002B4FCC /* CardStackBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89AF78BF2447E285002B4FCC /* CardStackBuilder.swift */; }; @@ -1421,9 +1421,9 @@ 89ADE12E226BDED40067222B /* DateRelativeBolusEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateRelativeBolusEntry.swift; sourceTree = ""; }; 89ADE133226BF0490067222B /* TestingScenarioInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestingScenarioInstance.swift; sourceTree = ""; }; 89ADE135226BF0BE0067222B /* DateRelativeGlucoseSample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateRelativeGlucoseSample.swift; sourceTree = ""; }; - 89AE221F228BC54C00BDFD85 /* TemporaryScheduleOverridePreset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverridePreset.swift; sourceTree = ""; }; + 89AE221F228BC54C00BDFD85 /* TemporaryPreset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemporaryPreset.swift; sourceTree = ""; }; 89AE2223228BC54C00BDFD85 /* TemporaryScheduleOverride.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverride.swift; sourceTree = ""; }; - 89AE2224228BC54C00BDFD85 /* TemporaryScheduleOverrideSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverrideSettings.swift; sourceTree = ""; }; + 89AE2224228BC54C00BDFD85 /* TemporaryPresetSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemporaryPresetSettings.swift; sourceTree = ""; }; 89AE2225228BC54C00BDFD85 /* TemporaryScheduleOverrideHistory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverrideHistory.swift; sourceTree = ""; }; 89AE222A228BC56A00BDFD85 /* WeakSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakSet.swift; sourceTree = ""; }; 89AF78BF2447E285002B4FCC /* CardStackBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardStackBuilder.swift; sourceTree = ""; }; @@ -2531,8 +2531,8 @@ A932803A2798D63B0091D0A1 /* SyncAlertObject.swift */, 89AE2223228BC54C00BDFD85 /* TemporaryScheduleOverride.swift */, 89AE2225228BC54C00BDFD85 /* TemporaryScheduleOverrideHistory.swift */, - 89AE221F228BC54C00BDFD85 /* TemporaryScheduleOverridePreset.swift */, - 89AE2224228BC54C00BDFD85 /* TemporaryScheduleOverrideSettings.swift */, + 89AE221F228BC54C00BDFD85 /* TemporaryPreset.swift */, + 89AE2224228BC54C00BDFD85 /* TemporaryPresetSettings.swift */, 1D1FCE2824BE4F11000300A8 /* TherapySetting.swift */, E9086B3824B3CB4B0062F5C8 /* TherapySettings.swift */, C1ABE38D2245CFCD00570E82 /* UnfairLock.swift */, @@ -4150,7 +4150,7 @@ 4322B783202FA2AF0002837D /* Reservoir.swift in Sources */, 1DA649AB2445174400F61E75 /* Alert.swift in Sources */, 4322B782202FA2AF0002837D /* PumpEventType.swift in Sources */, - 89AE2228228BC54C00BDFD85 /* TemporaryScheduleOverrideSettings.swift in Sources */, + 89AE2228228BC54C00BDFD85 /* TemporaryPresetSettings.swift in Sources */, 43D8FDFA1C7290350073BE78 /* GlucoseRangeSchedule.swift in Sources */, 4322B77E202FA2AF0002837D /* PersistedPumpEvent.swift in Sources */, 437AFEEE2036A156008C4892 /* CachedCarbObject+CoreDataProperties.swift in Sources */, @@ -4159,7 +4159,7 @@ 4322B77B202FA2790002837D /* StoredCarbEntry.swift in Sources */, 4322B78C202FA2B30002837D /* HKQuantitySample+InsulinKit.swift in Sources */, C187339229B9486200519CDF /* MutableCollection.swift in Sources */, - 89AE2226228BC54C00BDFD85 /* TemporaryScheduleOverridePreset.swift in Sources */, + 89AE2226228BC54C00BDFD85 /* TemporaryPreset.swift in Sources */, 434C5F9C2098352500B2FD1A /* QuantityFormatter.swift in Sources */, 43F503642106C78C009FA89A /* ServiceAuthentication.swift in Sources */, A93761C125ED670200F6BE43 /* BluetoothProvider.swift in Sources */, @@ -4407,8 +4407,8 @@ A933DB8924BF97CC009B417A /* CriticalEventLog.swift in Sources */, 89AE2238228BCAAB00BDFD85 /* TemporaryScheduleOverride.swift in Sources */, 89AE2239228BCAAB00BDFD85 /* TemporaryScheduleOverrideHistory.swift in Sources */, - 89AE223A228BCAAB00BDFD85 /* TemporaryScheduleOverridePreset.swift in Sources */, - 89AE223B228BCAAB00BDFD85 /* TemporaryScheduleOverrideSettings.swift in Sources */, + 89AE223A228BCAAB00BDFD85 /* TemporaryPreset.swift in Sources */, + 89AE223B228BCAAB00BDFD85 /* TemporaryPresetSettings.swift in Sources */, A987CD4724A5893500439ADC /* JSONStreamEncoder.swift in Sources */, C1CEBFD929BA9D81007FD8A3 /* TimeZone.swift in Sources */, 89AE223C228BCAAB00BDFD85 /* WeakSet.swift in Sources */, diff --git a/LoopKit/DailyQuantitySchedule+Override.swift b/LoopKit/DailyQuantitySchedule+Override.swift index 7af3abea2..f1ff94112 100644 --- a/LoopKit/DailyQuantitySchedule+Override.swift +++ b/LoopKit/DailyQuantitySchedule+Override.swift @@ -66,7 +66,7 @@ extension DailyValueSchedule where T == Double { fileprivate func applyingOverride( _ override: TemporaryScheduleOverride, relativeTo date: Date, - multiplier multiplierKeyPath: KeyPath + multiplier multiplierKeyPath: KeyPath ) -> DailyValueSchedule { guard let multiplier = override.settings[keyPath: multiplierKeyPath] else { return self diff --git a/LoopKit/SettingsStore.swift b/LoopKit/SettingsStore.swift index c1ec0a2cf..cb2162887 100644 --- a/LoopKit/SettingsStore.swift +++ b/LoopKit/SettingsStore.swift @@ -296,7 +296,7 @@ public struct StoredSettings: Equatable { public var preMealTargetRange: ClosedRange? public var workoutTargetRange: ClosedRange? public var workoutDefaultDuration: TemporaryScheduleOverride.Duration? - public var overridePresets: [TemporaryScheduleOverridePreset] + public var overridePresets: [TemporaryPreset] public var maximumBasalRatePerHour: Double? public var maximumBolus: Double? public var suspendThreshold: GlucoseThreshold? @@ -322,7 +322,7 @@ public struct StoredSettings: Equatable { preMealTargetRange: ClosedRange? = nil, workoutTargetRange: ClosedRange? = nil, workoutDefaultDuration: TemporaryScheduleOverride.Duration? = nil, - overridePresets: [TemporaryScheduleOverridePreset] = [], + overridePresets: [TemporaryPreset] = [], maximumBasalRatePerHour: Double? = nil, maximumBolus: Double? = nil, suspendThreshold: GlucoseThreshold? = nil, @@ -380,7 +380,7 @@ extension StoredSettings: Codable { preMealTargetRange: try container.decodeIfPresent(DoubleRange.self, forKey: .preMealTargetRange)?.quantityRange(for: bloodGlucoseUnit), workoutTargetRange: try container.decodeIfPresent(DoubleRange.self, forKey: .workoutTargetRange)?.quantityRange(for: bloodGlucoseUnit), workoutDefaultDuration: try container.decodeIfPresent(TemporaryScheduleOverride.Duration.self, forKey: .workoutDefaultDuration), - overridePresets: try container.decodeIfPresent([TemporaryScheduleOverridePreset].self, forKey: .overridePresets) ?? [], + overridePresets: try container.decodeIfPresent([TemporaryPreset].self, forKey: .overridePresets) ?? [], maximumBasalRatePerHour: try container.decodeIfPresent(Double.self, forKey: .maximumBasalRatePerHour), maximumBolus: try container.decodeIfPresent(Double.self, forKey: .maximumBolus), suspendThreshold: try container.decodeIfPresent(GlucoseThreshold.self, forKey: .suspendThreshold), diff --git a/LoopKit/TemporaryPreset.swift b/LoopKit/TemporaryPreset.swift new file mode 100644 index 000000000..dd52cc621 --- /dev/null +++ b/LoopKit/TemporaryPreset.swift @@ -0,0 +1,137 @@ +// +// TemporaryPreset.swift +// Loop +// +// Created by Michael Pangburn on 1/2/19. +// Copyright © 2019 LoopKit Authors. All rights reserved. +// + +import Foundation + +public struct PresetScheduleRepeatOptions: OptionSet, Sendable, Hashable { + public let rawValue: UInt8 + + public static let none = PresetScheduleRepeatOptions([]) + public static let sunday = PresetScheduleRepeatOptions(rawValue: 1 << 0) + public static let monday = PresetScheduleRepeatOptions(rawValue: 1 << 1) + public static let tuesday = PresetScheduleRepeatOptions(rawValue: 1 << 2) + public static let wednesday = PresetScheduleRepeatOptions(rawValue: 1 << 3) + public static let thursday = PresetScheduleRepeatOptions(rawValue: 1 << 4) + public static let friday = PresetScheduleRepeatOptions(rawValue: 1 << 5) + public static let saturday = PresetScheduleRepeatOptions(rawValue: 1 << 6) + + public static let allCases: [PresetScheduleRepeatOptions] = [ + .sunday, + .monday, + .tuesday, + .wednesday, + .thursday, + .friday, + .saturday, + ] + + public init(rawValue: UInt8) { + self.rawValue = rawValue + } + + // Helper to map OptionSet to calendar weekday index (Sunday = 1 in Calendar) + public var calendarWeekdayIndex: Int? { + switch self { + case .sunday: return 1 + case .monday: return 2 + case .tuesday: return 3 + case .wednesday: return 4 + case .thursday: return 5 + case .friday: return 6 + case .saturday: return 7 + default: return nil + } + } +} + +extension PresetScheduleRepeatOptions: Codable {} + +public struct TemporaryPreset: Hashable, Sendable { + public let id: UUID + public var symbol: String + public var name: String + public var settings: TemporaryPresetSettings + public var duration: TemporaryScheduleOverride.Duration + public var scheduleStartDate: Date? + public var repeatOptions: PresetScheduleRepeatOptions? + + public init(id: UUID = UUID(), symbol: String, name: String, settings: TemporaryPresetSettings, duration: TemporaryScheduleOverride.Duration, scheduleStartDate: Date? = nil, repeatOptions: PresetScheduleRepeatOptions = .none) { + self.id = id + self.symbol = symbol + self.name = name + self.settings = settings + self.duration = duration + self.scheduleStartDate = scheduleStartDate + self.repeatOptions = repeatOptions + } + + public func createOverride(enactTrigger: TemporaryScheduleOverride.EnactTrigger, beginningAt date: Date = Date()) -> TemporaryScheduleOverride { + return TemporaryScheduleOverride( + context: .preset(self), + settings: settings, + startDate: date, + duration: duration, + enactTrigger: enactTrigger, + syncIdentifier: UUID() + ) + } +} + +extension TemporaryPreset: RawRepresentable { + public typealias RawValue = [String: Any] + + public init?(rawValue: RawValue) { + guard + let idString = rawValue["id"] as? String, + let id = UUID(uuidString: idString), + let symbol = rawValue["symbol"] as? String, + let name = rawValue["name"] as? String, + let settingsRawValue = rawValue["settings"] as? TemporaryPresetSettings.RawValue, + let settings = TemporaryPresetSettings(rawValue: settingsRawValue), + let durationRawValue = rawValue["duration"] as? TemporaryScheduleOverride.Duration.RawValue, + let duration = TemporaryScheduleOverride.Duration(rawValue: durationRawValue) + else { + return nil + } + + let scheduleStartDate = rawValue["scheduleStartDate"] as? Date + let rawRepeatOptions = rawValue["repeatOptions"] as? PresetScheduleRepeatOptions.RawValue + + self.init( + id: id, + symbol: symbol, + name: name, + settings: settings, + duration: duration, + scheduleStartDate: scheduleStartDate, + repeatOptions: rawRepeatOptions.flatMap(PresetScheduleRepeatOptions.init) ?? .none + ) + } + + public var rawValue: RawValue { + var rval: RawValue = [ + "id": id.uuidString, + "symbol": symbol, + "name": name, + "settings": settings.rawValue, + "duration": duration.rawValue + ] + + if let scheduleStartDate { + rval["scheduleStartDate"] = scheduleStartDate + } + + if let repeatOptions { + rval["repeatOptions"] = repeatOptions.rawValue + } + + return rval + } +} + +extension TemporaryPreset: Codable {} diff --git a/LoopKit/TemporaryScheduleOverrideSettings.swift b/LoopKit/TemporaryPresetSettings.swift similarity index 93% rename from LoopKit/TemporaryScheduleOverrideSettings.swift rename to LoopKit/TemporaryPresetSettings.swift index b09ad24a3..3391d63b2 100644 --- a/LoopKit/TemporaryScheduleOverrideSettings.swift +++ b/LoopKit/TemporaryPresetSettings.swift @@ -9,7 +9,7 @@ import HealthKit import LoopAlgorithm -public struct TemporaryScheduleOverrideSettings: Hashable, Sendable { +public struct TemporaryPresetSettings: Hashable, Sendable { private var targetRangeInMgdl: DoubleRange? public var insulinNeedsScaleFactor: Double? @@ -43,7 +43,7 @@ public struct TemporaryScheduleOverrideSettings: Hashable, Sendable { } } -extension TemporaryScheduleOverrideSettings: RawRepresentable { +extension TemporaryPresetSettings: RawRepresentable { public typealias RawValue = [String: Any] private enum Key { @@ -84,4 +84,4 @@ extension TemporaryScheduleOverrideSettings: RawRepresentable { } } -extension TemporaryScheduleOverrideSettings: Codable {} +extension TemporaryPresetSettings: Codable {} diff --git a/LoopKit/TemporaryScheduleOverride.swift b/LoopKit/TemporaryScheduleOverride.swift index 51ca099bd..325fc6281 100644 --- a/LoopKit/TemporaryScheduleOverride.swift +++ b/LoopKit/TemporaryScheduleOverride.swift @@ -14,7 +14,7 @@ public struct TemporaryScheduleOverride: Hashable, Sendable { public enum Context: Hashable, Sendable { case preMeal case legacyWorkout - case preset(TemporaryScheduleOverridePreset) + case preset(TemporaryPreset) case custom } @@ -50,7 +50,7 @@ public struct TemporaryScheduleOverride: Hashable, Sendable { } public var context: Context - public var settings: TemporaryScheduleOverrideSettings + public var settings: TemporaryPresetSettings public var startDate: Date public let enactTrigger: EnactTrigger public let syncIdentifier: UUID @@ -112,7 +112,7 @@ public struct TemporaryScheduleOverride: Hashable, Sendable { public init( context: Context, - settings: TemporaryScheduleOverrideSettings, + settings: TemporaryPresetSettings, startDate: Date, duration: Duration, enactTrigger: EnactTrigger, @@ -141,8 +141,8 @@ extension TemporaryScheduleOverride: RawRepresentable { guard let contextRawValue = rawValue["context"] as? Context.RawValue, let context = Context(rawValue: contextRawValue), - let settingsRawValue = rawValue["settings"] as? TemporaryScheduleOverrideSettings.RawValue, - let settings = TemporaryScheduleOverrideSettings(rawValue: settingsRawValue), + let settingsRawValue = rawValue["settings"] as? TemporaryPresetSettings.RawValue, + let settings = TemporaryPresetSettings(rawValue: settingsRawValue), let startDateSeconds = rawValue["startDate"] as? TimeInterval, let durationRawValue = rawValue["duration"] as? Duration.RawValue, let duration = Duration(rawValue: durationRawValue) @@ -201,8 +201,8 @@ extension TemporaryScheduleOverride.Context: RawRepresentable { self = .legacyWorkout case "preset": guard - let presetRawValue = rawValue["preset"] as? TemporaryScheduleOverridePreset.RawValue, - let preset = TemporaryScheduleOverridePreset(rawValue: presetRawValue) + let presetRawValue = rawValue["preset"] as? TemporaryPreset.RawValue, + let preset = TemporaryPreset(rawValue: presetRawValue) else { return nil } @@ -272,7 +272,7 @@ extension TemporaryScheduleOverride.Context: Codable { } private struct Preset: Codable { - let preset: TemporaryScheduleOverridePreset + let preset: TemporaryPreset } private enum CodableKeys: String, CodingKey { diff --git a/LoopKit/TemporaryScheduleOverrideHistory.swift b/LoopKit/TemporaryScheduleOverrideHistory.swift index 0f590e10f..ee0598928 100644 --- a/LoopKit/TemporaryScheduleOverrideHistory.swift +++ b/LoopKit/TemporaryScheduleOverrideHistory.swift @@ -378,7 +378,7 @@ public class TemporaryScheduleOverrideHistoryContainer { return persisted } catch { - fatalError() + fatalError(error.localizedDescription) } } } diff --git a/LoopKit/TemporaryScheduleOverridePreset.swift b/LoopKit/TemporaryScheduleOverridePreset.swift deleted file mode 100644 index fe050ac7f..000000000 --- a/LoopKit/TemporaryScheduleOverridePreset.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// TemporaryScheduleOverridePreset.swift -// Loop -// -// Created by Michael Pangburn on 1/2/19. -// Copyright © 2019 LoopKit Authors. All rights reserved. -// - -import Foundation - - -public struct TemporaryScheduleOverridePreset: Hashable, Sendable { - public let id: UUID - public var symbol: String - public var name: String - public var settings: TemporaryScheduleOverrideSettings - public var duration: TemporaryScheduleOverride.Duration - - public init(id: UUID = UUID(), symbol: String, name: String, settings: TemporaryScheduleOverrideSettings, duration: TemporaryScheduleOverride.Duration) { - self.id = id - self.symbol = symbol - self.name = name - self.settings = settings - self.duration = duration - } - - public func createOverride(enactTrigger: TemporaryScheduleOverride.EnactTrigger, beginningAt date: Date = Date()) -> TemporaryScheduleOverride { - return TemporaryScheduleOverride( - context: .preset(self), - settings: settings, - startDate: date, - duration: duration, - enactTrigger: enactTrigger, - syncIdentifier: UUID() - ) - } -} - -extension TemporaryScheduleOverridePreset: RawRepresentable { - public typealias RawValue = [String: Any] - - public init?(rawValue: RawValue) { - guard - let idString = rawValue["id"] as? String, - let id = UUID(uuidString: idString), - let symbol = rawValue["symbol"] as? String, - let name = rawValue["name"] as? String, - let settingsRawValue = rawValue["settings"] as? TemporaryScheduleOverrideSettings.RawValue, - let settings = TemporaryScheduleOverrideSettings(rawValue: settingsRawValue), - let durationRawValue = rawValue["duration"] as? TemporaryScheduleOverride.Duration.RawValue, - let duration = TemporaryScheduleOverride.Duration(rawValue: durationRawValue) - else { - return nil - } - - self.init(id: id, symbol: symbol, name: name, settings: settings, duration: duration) - } - - public var rawValue: RawValue { - return [ - "id": id.uuidString, - "symbol": symbol, - "name": name, - "settings": settings.rawValue, - "duration": duration.rawValue - ] - } -} - -extension TemporaryScheduleOverridePreset: Codable {} diff --git a/LoopKit/TherapySettings.swift b/LoopKit/TherapySettings.swift index b2c7eceea..9eec2818c 100644 --- a/LoopKit/TherapySettings.swift +++ b/LoopKit/TherapySettings.swift @@ -15,7 +15,7 @@ public struct TherapySettings: Equatable { public var correctionRangeOverrides: CorrectionRangeOverrides? - public var overridePresets: [TemporaryScheduleOverridePreset]? + public var overridePresets: [TemporaryPreset]? public var maximumBasalRatePerHour: Double? @@ -47,7 +47,7 @@ public struct TherapySettings: Equatable { public init( glucoseTargetRangeSchedule: GlucoseRangeSchedule? = nil, correctionRangeOverrides: CorrectionRangeOverrides? = nil, - overridePresets: [TemporaryScheduleOverridePreset]? = nil, + overridePresets: [TemporaryPreset]? = nil, maximumBasalRatePerHour: Double? = nil, maximumBolus: Double? = nil, suspendThreshold: GlucoseThreshold? = nil, diff --git a/LoopKitTests/DosingDecisionStoreTests.swift b/LoopKitTests/DosingDecisionStoreTests.swift index fcf77af4d..8e66c4251 100644 --- a/LoopKitTests/DosingDecisionStoreTests.swift +++ b/LoopKitTests/DosingDecisionStoreTests.swift @@ -1027,7 +1027,7 @@ fileprivate extension StoredDosingDecision { let reason = "test" let settings = StoredDosingDecision.Settings(syncIdentifier: UUID(uuidString: "2B03D96C-99CD-4140-99CD-80C3E64D6011")!) let scheduleOverride = TemporaryScheduleOverride(context: .preMeal, - settings: TemporaryScheduleOverrideSettings(unit: .milligramsPerDeciliter, + settings: TemporaryPresetSettings(unit: .milligramsPerDeciliter, targetRange: DoubleRange(minValue: 80.0, maxValue: 90.0), insulinNeedsScaleFactor: 1.5), diff --git a/LoopKitTests/SettingsStoreTests.swift b/LoopKitTests/SettingsStoreTests.swift index 49253aab1..16216d0e8 100644 --- a/LoopKitTests/SettingsStoreTests.swift +++ b/LoopKitTests/SettingsStoreTests.swift @@ -930,10 +930,10 @@ fileprivate extension StoredSettings { end: dateFormatter.date(from: "2020-05-14T14:48:15Z")!)) let preMealTargetRange = DoubleRange(minValue: 80.0, maxValue: 90.0).quantityRange(for: .milligramsPerDeciliter) let workoutTargetRange = DoubleRange(minValue: 150.0, maxValue: 160.0).quantityRange(for: .milligramsPerDeciliter) - let overridePresets = [TemporaryScheduleOverridePreset(id: UUID(uuidString: "2A67A303-5203-4CB8-8263-79498265368E")!, + let overridePresets = [TemporaryPreset(id: UUID(uuidString: "2A67A303-5203-4CB8-8263-79498265368E")!, symbol: "🍎", name: "Apple", - settings: TemporaryScheduleOverrideSettings(unit: .milligramsPerDeciliter, + settings: TemporaryPresetSettings(unit: .milligramsPerDeciliter, targetRange: DoubleRange(minValue: 130.0, maxValue: 140.0), insulinNeedsScaleFactor: 2.0), duration: .finite(.minutes(60)))] diff --git a/LoopKitTests/TemporaryScheduleOverrideHistoryTests.swift b/LoopKitTests/TemporaryScheduleOverrideHistoryTests.swift index 3454bc4d0..833259555 100644 --- a/LoopKitTests/TemporaryScheduleOverrideHistoryTests.swift +++ b/LoopKitTests/TemporaryScheduleOverrideHistoryTests.swift @@ -33,7 +33,7 @@ final class TemporaryScheduleOverrideHistoryTests: XCTestCase { insulinNeedsScaleFactor scaleFactor: Double, recordedAt enableDateOffset: TimeInterval? = nil ) { - let settings = TemporaryScheduleOverrideSettings(unit: .milligramsPerDeciliter, targetRange: nil, insulinNeedsScaleFactor: scaleFactor) + let settings = TemporaryPresetSettings(unit: .milligramsPerDeciliter, targetRange: nil, insulinNeedsScaleFactor: scaleFactor) let override = TemporaryScheduleOverride(context: .custom, settings: settings, startDate: referenceDate + offset, duration: duration, enactTrigger: .local, syncIdentifier: UUID()) let enableDate: Date if let enableDateOffset = enableDateOffset { diff --git a/LoopKitTests/TemporaryScheduleOverrideTests.swift b/LoopKitTests/TemporaryScheduleOverrideTests.swift index e54e38fe3..10982a057 100644 --- a/LoopKitTests/TemporaryScheduleOverrideTests.swift +++ b/LoopKitTests/TemporaryScheduleOverrideTests.swift @@ -52,7 +52,7 @@ class TemporaryScheduleOverrideTests: XCTestCase { private func basalUpOverride(start: String, end: String) -> TemporaryScheduleOverride { return TemporaryScheduleOverride( context: .custom, - settings: TemporaryScheduleOverrideSettings( + settings: TemporaryPresetSettings( unit: .milligramsPerDeciliter, targetRange: nil, insulinNeedsScaleFactor: 1.5 @@ -255,7 +255,7 @@ class TemporaryScheduleOverrideTests: XCTestCase { let overrideRange = DoubleRange(minValue: 120, maxValue: 140) let overrideStart = Date() let overrideDuration = TimeInterval(hours: 4) - let settings = TemporaryScheduleOverrideSettings(unit: .milligramsPerDeciliter, targetRange: overrideRange) + let settings = TemporaryPresetSettings(unit: .milligramsPerDeciliter, targetRange: overrideRange) let override = TemporaryScheduleOverride(context: .custom, settings: settings, startDate: overrideStart, duration: .finite(overrideDuration), enactTrigger: .local, syncIdentifier: UUID()) let normalRange = DoubleRange(minValue: 95, maxValue: 105) let rangeSchedule = GlucoseRangeSchedule(unit: .milligramsPerDeciliter, dailyItems: [RepeatingScheduleValue(startTime: 0, value: normalRange)])!.applyingOverride(override) @@ -270,7 +270,7 @@ class TemporaryScheduleOverrideTests: XCTestCase { let overrideRange = DoubleRange(minValue: 120, maxValue: 140) let overrideStart = Date() + .hours(2) let overrideDuration = TimeInterval(hours: 4) - let settings = TemporaryScheduleOverrideSettings(unit: .milligramsPerDeciliter, targetRange: overrideRange) + let settings = TemporaryPresetSettings(unit: .milligramsPerDeciliter, targetRange: overrideRange) let futureOverride = TemporaryScheduleOverride(context: .custom, settings: settings, startDate: overrideStart, duration: .finite(overrideDuration), enactTrigger: .local, syncIdentifier: UUID()) let normalRange = DoubleRange(minValue: 95, maxValue: 105) let rangeSchedule = GlucoseRangeSchedule(unit: .milligramsPerDeciliter, dailyItems: [RepeatingScheduleValue(startTime: 0, value: normalRange)])!.applyingOverride(futureOverride) @@ -511,7 +511,7 @@ extension TemporaryScheduleOverride { lower: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: $0.lowerBound), upper: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: $0.upperBound))) } - let settings = TemporaryScheduleOverrideSettings(targetRange: targetRange, insulinNeedsScaleFactor: scale) + let settings = TemporaryPresetSettings(targetRange: targetRange, insulinNeedsScaleFactor: scale) let duration: TimeInterval? = end.map { $0.timeIntervalSince(start) } return TemporaryScheduleOverride( context: .custom, @@ -544,10 +544,10 @@ class TemporaryScheduleOverrideContextCodableTests: XCTestCase { } func testCodablePreset() throws { - let preset = TemporaryScheduleOverridePreset(id: UUID(uuidString: "238E41EA-9576-4981-A1A4-51E10228584F")!, + let preset = TemporaryPreset(id: UUID(uuidString: "238E41EA-9576-4981-A1A4-51E10228584F")!, symbol: "🚀", name: "Rocket", - settings: TemporaryScheduleOverrideSettings(unit: .milligramsPerDeciliter, + settings: TemporaryPresetSettings(unit: .milligramsPerDeciliter, targetRange: DoubleRange(minValue: 90, maxValue: 100)), duration: .indefinite) try assertTemporaryScheduleOverrideContextCodable(.preset(preset), encodesJSON: """ diff --git a/LoopKitUI/View Controllers/AddEditOverrideTableViewController.swift b/LoopKitUI/View Controllers/AddEditOverrideTableViewController.swift index 3905a52b1..ce0186cb4 100644 --- a/LoopKitUI/View Controllers/AddEditOverrideTableViewController.swift +++ b/LoopKitUI/View Controllers/AddEditOverrideTableViewController.swift @@ -12,14 +12,14 @@ import LoopKit import LoopAlgorithm public protocol AddEditOverrideTableViewControllerDelegate: AnyObject { - func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryScheduleOverridePreset) + func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryPreset) func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSaveOverride override: TemporaryScheduleOverride) func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didCancelOverride override: TemporaryScheduleOverride) } // MARK: - Default Implementations extension AddEditOverrideTableViewControllerDelegate { - public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryScheduleOverridePreset) { } + public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryPreset) { } public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSaveOverride override: TemporaryScheduleOverride) { } public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didCancelOverride override: TemporaryScheduleOverride) { } } @@ -35,8 +35,8 @@ public final class AddEditOverrideTableViewController: UITableViewController { public enum InputMode { case newPreset // Creating a new preset - case editPreset(TemporaryScheduleOverridePreset) // Editing an existing preset - case customizePresetOverride(TemporaryScheduleOverridePreset) // Defining an override relative to an existing preset + case editPreset(TemporaryPreset) // Editing an existing preset + case customizePresetOverride(TemporaryPreset) // Defining an override relative to an existing preset case customOverride // Defining a one-off custom override case editOverride(TemporaryScheduleOverride) // Editing an active override case viewOverride(TemporaryScheduleOverride) // Viewing an override @@ -130,7 +130,7 @@ public final class AddEditOverrideTableViewController: UITableViewController { } } - private func configure(with settings: TemporaryScheduleOverrideSettings) { + private func configure(with settings: TemporaryPresetSettings) { if let targetRange = settings.targetRange { self.targetRange = DoubleRange(minValue: targetRange.lowerBound.doubleValue(for: glucoseUnit), maxValue: targetRange.upperBound.doubleValue(for: glucoseUnit)) } else { @@ -486,7 +486,7 @@ extension AddEditOverrideTableViewController { } } - private var configuredSettings: TemporaryScheduleOverrideSettings? { + private var configuredSettings: TemporaryPresetSettings? { if let targetRange = targetRange { guard targetRange.maxValue >= targetRange.minValue else { return nil @@ -497,14 +497,14 @@ extension AddEditOverrideTableViewController { } } - return TemporaryScheduleOverrideSettings( + return TemporaryPresetSettings( unit: glucoseUnit, targetRange: targetRange, insulinNeedsScaleFactor: insulinNeedsScaleFactor == 1.0 ? nil : insulinNeedsScaleFactor ) } - private var configuredPreset: TemporaryScheduleOverridePreset? { + private var configuredPreset: TemporaryPreset? { guard let symbol = symbol, !symbol.isEmpty, let name = name, !name.isEmpty, @@ -520,7 +520,7 @@ extension AddEditOverrideTableViewController { id = UUID() } - return TemporaryScheduleOverridePreset(id: id, symbol: symbol, name: name, settings: settings, duration: duration) + return TemporaryPreset(id: id, symbol: symbol, name: name, settings: settings, duration: duration) } private var configuredOverride: TemporaryScheduleOverride? { @@ -531,7 +531,7 @@ extension AddEditOverrideTableViewController { let context: TemporaryScheduleOverride.Context switch inputMode { case .customizePresetOverride(let preset): - let customizedPreset = TemporaryScheduleOverridePreset( + let customizedPreset = TemporaryPreset( symbol: preset.symbol, name: preset.name, settings: settings, diff --git a/LoopKitUI/View Controllers/OverrideSelectionViewController.swift b/LoopKitUI/View Controllers/OverrideSelectionViewController.swift index a67eedcb3..a84672cc0 100644 --- a/LoopKitUI/View Controllers/OverrideSelectionViewController.swift +++ b/LoopKitUI/View Controllers/OverrideSelectionViewController.swift @@ -15,8 +15,8 @@ import os.log public protocol OverrideSelectionViewControllerDelegate: AnyObject { - func overrideSelectionViewController(_ vc: OverrideSelectionViewController, didUpdatePresets presets: [TemporaryScheduleOverridePreset]) - func overrideSelectionViewController(_ vc: OverrideSelectionViewController, didConfirmPreset preset: TemporaryScheduleOverridePreset) + func overrideSelectionViewController(_ vc: OverrideSelectionViewController, didUpdatePresets presets: [TemporaryPreset]) + func overrideSelectionViewController(_ vc: OverrideSelectionViewController, didConfirmPreset preset: TemporaryPreset) func overrideSelectionViewController(_ vc: OverrideSelectionViewController, didConfirmOverride override: TemporaryScheduleOverride) func overrideSelectionViewController(_ vc: OverrideSelectionViewController, didCancelOverride override: TemporaryScheduleOverride) } @@ -27,7 +27,7 @@ public final class OverrideSelectionViewController: UICollectionViewController, public var scheduledOverride: TemporaryScheduleOverride? - public var presets: [TemporaryScheduleOverridePreset] = [] { + public var presets: [TemporaryPreset] = [] { didSet { delegate?.overrideSelectionViewController(self, didUpdatePresets: presets) } @@ -78,7 +78,7 @@ public final class OverrideSelectionViewController: UICollectionViewController, private enum CellContent { case scheduledOverride(TemporaryScheduleOverride) - case preset(TemporaryScheduleOverridePreset) + case preset(TemporaryPreset) case customOverride case history } @@ -220,7 +220,7 @@ public final class OverrideSelectionViewController: UICollectionViewController, } } - private func configure(_ cell: OverridePresetCollectionViewCell, with settings: TemporaryScheduleOverrideSettings, duration: TemporaryScheduleOverride.Duration) { + private func configure(_ cell: OverridePresetCollectionViewCell, with settings: TemporaryPresetSettings, duration: TemporaryScheduleOverride.Duration) { if let targetRange = settings.targetRange { cell.targetRangeLabel.text = makeTargetRangeText(from: targetRange) } else { @@ -441,7 +441,7 @@ extension OverrideSelectionViewController: UICollectionViewDelegateFlowLayout { } extension OverrideSelectionViewController: AddEditOverrideTableViewControllerDelegate { - public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryScheduleOverridePreset) { + public func addEditOverrideTableViewController(_ vc: AddEditOverrideTableViewController, didSavePreset preset: TemporaryPreset) { if let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first { presets[selectedIndexPath.row] = preset collectionView.reloadItems(at: [selectedIndexPath]) diff --git a/LoopKitUI/ViewModels/TherapySettingsViewModel.swift b/LoopKitUI/ViewModels/TherapySettingsViewModel.swift index 1abd583d3..7e564726c 100644 --- a/LoopKitUI/ViewModels/TherapySettingsViewModel.swift +++ b/LoopKitUI/ViewModels/TherapySettingsViewModel.swift @@ -155,7 +155,7 @@ extension TherapySettingsViewModel { therapySettings.overridePresets = presets.map { preset in if let targetRange = preset.settings.targetRange { var newPreset = preset - newPreset.settings = TemporaryScheduleOverrideSettings( + newPreset.settings = TemporaryPresetSettings( targetRange: ClosedRange.init( uncheckedBounds: ( lower: max(quantity, targetRange.lowerBound), From 1d595daa8c9e83d116d8ba730d3944e2dc115f51 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Tue, 22 Apr 2025 11:53:21 -0500 Subject: [PATCH 2/2] Fix unit tests --- LoopKitTests/SettingsStoreTests.swift | 2 ++ LoopKitTests/TemporaryScheduleOverrideTests.swift | 1 + 2 files changed, 3 insertions(+) diff --git a/LoopKitTests/SettingsStoreTests.swift b/LoopKitTests/SettingsStoreTests.swift index 16216d0e8..2da6c2638 100644 --- a/LoopKitTests/SettingsStoreTests.swift +++ b/LoopKitTests/SettingsStoreTests.swift @@ -285,6 +285,7 @@ class SettingsStorePersistenceTests: PersistenceControllerTestCase, SettingsStor }, "id" : "2A67A303-5203-4CB8-8263-79498265368E", "name" : "Apple", + "repeatOptions" : 0, "settings" : { "insulinNeedsScaleFactor" : 2, "targetRangeInMgdl" : { @@ -854,6 +855,7 @@ class StoredSettingsCodableTests: XCTestCase { }, "id" : "2A67A303-5203-4CB8-8263-79498265368E", "name" : "Apple", + "repeatOptions" : 0, "settings" : { "insulinNeedsScaleFactor" : 2, "targetRangeInMgdl" : { diff --git a/LoopKitTests/TemporaryScheduleOverrideTests.swift b/LoopKitTests/TemporaryScheduleOverrideTests.swift index 10982a057..d0711b30a 100644 --- a/LoopKitTests/TemporaryScheduleOverrideTests.swift +++ b/LoopKitTests/TemporaryScheduleOverrideTests.swift @@ -558,6 +558,7 @@ class TemporaryScheduleOverrideContextCodableTests: XCTestCase { "duration" : "indefinite", "id" : "238E41EA-9576-4981-A1A4-51E10228584F", "name" : "Rocket", + "repeatOptions" : 0, "settings" : { "targetRangeInMgdl" : { "maxValue" : 100,