Просмотр исходного кода

Merge pull request #157 from dnzxy/minor-contact-fixes

Minor Contact Trick Adjustments
polscm32 1 год назад
Родитель
Сommit
03892e8100
23 измененных файлов с 466 добавлено и 467 удалено
  1. 49 49
      FreeAPS.xcodeproj/project.pbxproj
  2. 0 1
      FreeAPS/Sources/APS/OpenAPS/Constants.swift
  3. 150 0
      FreeAPS/Sources/APS/Storage/ContactImageStorage.swift
  4. 0 150
      FreeAPS/Sources/APS/Storage/ContactTrickStorage.swift
  5. 1 1
      FreeAPS/Sources/Assemblies/ServiceAssembly.swift
  6. 1 1
      FreeAPS/Sources/Assemblies/StorageAssembly.swift
  7. 15 15
      FreeAPS/Sources/Models/ContactTrickEntry.swift
  8. 8 0
      FreeAPS/Sources/Modules/ContactImage/ContactImageDataFlow.swift
  9. 6 0
      FreeAPS/Sources/Modules/ContactImage/ContactImageProvider.swift
  10. 49 49
      FreeAPS/Sources/Modules/ContactTrick/ContactTrickStateModel.swift
  11. 42 35
      FreeAPS/Sources/Modules/ContactTrick/View/AddContactTrickSheet.swift
  12. 50 41
      FreeAPS/Sources/Modules/ContactTrick/View/ContactTrickDetailView.swift
  13. 10 10
      FreeAPS/Sources/Modules/ContactTrick/View/ContactTrickRootView.swift
  14. 0 8
      FreeAPS/Sources/Modules/ContactTrick/ContactTrickDataFlow.swift
  15. 0 6
      FreeAPS/Sources/Modules/ContactTrick/ContactTrickProvider.swift
  16. 2 2
      FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigAppleWatchView.swift
  17. 3 3
      FreeAPS/Sources/Router/Screen.swift
  18. 25 39
      FreeAPS/Sources/Services/ContactTrick/ContactTrickManager.swift
  19. 1 1
      FreeAPS/Sources/Services/ContactTrick/ContactTrickState.swift
  20. 49 51
      FreeAPS/Sources/Services/ContactTrick/ContactTrickPicture.swift
  21. 1 1
      Model/Classes+Properties/ContactTrickEntryStored+CoreDataClass.swift
  22. 3 3
      Model/Classes+Properties/ContactTrickEntryStored+CoreDataProperties.swift
  23. 1 1
      Model/TrioCoreDataPersistentContainer.xcdatamodeld/TrioCoreDataPersistentContainer.xcdatamodel/contents

+ 49 - 49
FreeAPS.xcodeproj/project.pbxproj

@@ -342,11 +342,11 @@
 		BDBAACFA2C2D439700370AAE /* OverrideData.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDBAACF92C2D439700370AAE /* OverrideData.swift */; };
 		BDC2EA452C3043B000E5BBD0 /* OverrideStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC2EA442C3043B000E5BBD0 /* OverrideStorage.swift */; };
 		BDC2EA472C3045AD00E5BBD0 /* Override.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC2EA462C3045AD00E5BBD0 /* Override.swift */; };
-		BDC530FF2D0F6BE300088832 /* ContactTrickManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC530FE2D0F6BE300088832 /* ContactTrickManager.swift */; };
-		BDC531122D1060FA00088832 /* ContactTrickDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531112D1060FA00088832 /* ContactTrickDetailView.swift */; };
-		BDC531142D10611D00088832 /* AddContactTrickSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531132D10611D00088832 /* AddContactTrickSheet.swift */; };
-		BDC531162D10629000088832 /* ContactTrickPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531152D10629000088832 /* ContactTrickPicture.swift */; };
-		BDC531182D1062F200088832 /* ContactTrickState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531172D1062F200088832 /* ContactTrickState.swift */; };
+		BDC530FF2D0F6BE300088832 /* ContactImageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC530FE2D0F6BE300088832 /* ContactImageManager.swift */; };
+		BDC531122D1060FA00088832 /* ContactImageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531112D1060FA00088832 /* ContactImageDetailView.swift */; };
+		BDC531142D10611D00088832 /* AddContactImageSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531132D10611D00088832 /* AddContactImageSheet.swift */; };
+		BDC531162D10629000088832 /* ContactPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531152D10629000088832 /* ContactPicture.swift */; };
+		BDC531182D1062F200088832 /* ContactImageState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC531172D1062F200088832 /* ContactImageState.swift */; };
 		BDCAF2382C639F35002DC907 /* SettingItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCAF2372C639F35002DC907 /* SettingItems.swift */; };
 		BDCD47AF2C1F3F1700F8BCD5 /* OverrideStored+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCD47AE2C1F3F1700F8BCD5 /* OverrideStored+helper.swift */; };
 		BDDAF9EF2D00554500B34E7A /* SelectionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDDAF9EE2D00553E00B34E7A /* SelectionPopoverView.swift */; };
@@ -484,7 +484,7 @@
 		DD9ECB712CA9A0BA00AA7C45 /* RemoteControlConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB6E2CA9A0BA00AA7C45 /* RemoteControlConfigProvider.swift */; };
 		DD9ECB722CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB6F2CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift */; };
 		DD9ECB742CA9A0C300AA7C45 /* RemoteControlConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB732CA9A0C300AA7C45 /* RemoteControlConfig.swift */; };
-		DDB37CC52D05048F00D99BF4 /* ContactTrickStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB37CC42D05048F00D99BF4 /* ContactTrickStorage.swift */; };
+		DDB37CC52D05048F00D99BF4 /* ContactImageStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */; };
 		DDB37CC72D05127500D99BF4 /* FontExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB37CC62D05127500D99BF4 /* FontExtensions.swift */; };
 		DDCEBF5B2CC1B76400DF4C36 /* LiveActivity+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCEBF5A2CC1B76400DF4C36 /* LiveActivity+Helper.swift */; };
 		DDD163122C4C689900CD525A /* AdjustmentsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD163112C4C689900CD525A /* AdjustmentsStateModel.swift */; };
@@ -543,10 +543,10 @@
 		E39E418C56A5A46B61D960EE /* ConfigEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */; };
 		E3A08AAE59538BC8A8ABE477 /* GlucoseNotificationSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3260468377DA9DB4DEE9AF6D /* GlucoseNotificationSettingsDataFlow.swift */; };
 		E592A3702CEEC01E009A472C /* ContactTrickEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A36F2CEEC01E009A472C /* ContactTrickEntry.swift */; };
-		E592A3772CEEC038009A472C /* ContactTrickStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3752CEEC038009A472C /* ContactTrickStateModel.swift */; };
-		E592A3782CEEC038009A472C /* ContactTrickDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3732CEEC038009A472C /* ContactTrickDataFlow.swift */; };
-		E592A3792CEEC038009A472C /* ContactTrickRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3712CEEC038009A472C /* ContactTrickRootView.swift */; };
-		E592A37A2CEEC038009A472C /* ContactTrickProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3742CEEC038009A472C /* ContactTrickProvider.swift */; };
+		E592A3772CEEC038009A472C /* ContactImageStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3752CEEC038009A472C /* ContactImageStateModel.swift */; };
+		E592A3782CEEC038009A472C /* ContactImageDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3732CEEC038009A472C /* ContactImageDataFlow.swift */; };
+		E592A3792CEEC038009A472C /* ContactImageRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3712CEEC038009A472C /* ContactImageRootView.swift */; };
+		E592A37A2CEEC038009A472C /* ContactImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3742CEEC038009A472C /* ContactImageProvider.swift */; };
 		E974172296125A5AE99E634C /* PumpConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD22C985B79A2F0D2EA3D9D /* PumpConfigRootView.swift */; };
 		F5CA3DB1F9DC8B05792BBFAA /* CGMDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */; };
 		F5F7E6C1B7F098F59EB67EC5 /* TargetsEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */; };
@@ -1042,11 +1042,11 @@
 		BDBAACF92C2D439700370AAE /* OverrideData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideData.swift; sourceTree = "<group>"; };
 		BDC2EA442C3043B000E5BBD0 /* OverrideStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideStorage.swift; sourceTree = "<group>"; };
 		BDC2EA462C3045AD00E5BBD0 /* Override.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Override.swift; sourceTree = "<group>"; };
-		BDC530FE2D0F6BE300088832 /* ContactTrickManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickManager.swift; sourceTree = "<group>"; };
-		BDC531112D1060FA00088832 /* ContactTrickDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickDetailView.swift; sourceTree = "<group>"; };
-		BDC531132D10611D00088832 /* AddContactTrickSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactTrickSheet.swift; sourceTree = "<group>"; };
-		BDC531152D10629000088832 /* ContactTrickPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickPicture.swift; sourceTree = "<group>"; };
-		BDC531172D1062F200088832 /* ContactTrickState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickState.swift; sourceTree = "<group>"; };
+		BDC530FE2D0F6BE300088832 /* ContactImageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageManager.swift; sourceTree = "<group>"; };
+		BDC531112D1060FA00088832 /* ContactImageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageDetailView.swift; sourceTree = "<group>"; };
+		BDC531132D10611D00088832 /* AddContactImageSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactImageSheet.swift; sourceTree = "<group>"; };
+		BDC531152D10629000088832 /* ContactPicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactPicture.swift; sourceTree = "<group>"; };
+		BDC531172D1062F200088832 /* ContactImageState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageState.swift; sourceTree = "<group>"; };
 		BDCAF2372C639F35002DC907 /* SettingItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingItems.swift; sourceTree = "<group>"; };
 		BDCD47AE2C1F3F1700F8BCD5 /* OverrideStored+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+helper.swift"; sourceTree = "<group>"; };
 		BDDAF9EE2D00553E00B34E7A /* SelectionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionPopoverView.swift; sourceTree = "<group>"; };
@@ -1188,7 +1188,7 @@
 		DD9ECB732CA9A0C300AA7C45 /* RemoteControlConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteControlConfig.swift; sourceTree = "<group>"; };
 		DDB37CC22D05044D00D99BF4 /* ContactTrickEntryStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContactTrickEntryStored+CoreDataClass.swift"; sourceTree = "<group>"; };
 		DDB37CC32D05044D00D99BF4 /* ContactTrickEntryStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContactTrickEntryStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
-		DDB37CC42D05048F00D99BF4 /* ContactTrickStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickStorage.swift; sourceTree = "<group>"; };
+		DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageStorage.swift; sourceTree = "<group>"; };
 		DDB37CC62D05127500D99BF4 /* FontExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontExtensions.swift; sourceTree = "<group>"; };
 		DDCEBF5A2CC1B76400DF4C36 /* LiveActivity+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LiveActivity+Helper.swift"; sourceTree = "<group>"; };
 		DDD163112C4C689900CD525A /* AdjustmentsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjustmentsStateModel.swift; sourceTree = "<group>"; };
@@ -1245,10 +1245,10 @@
 		E0D4F80427513ECF00BDF1FE /* HealthKitSample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthKitSample.swift; sourceTree = "<group>"; };
 		E26904AACA8D9C15D229D675 /* SnoozeStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeStateModel.swift; sourceTree = "<group>"; };
 		E592A36F2CEEC01E009A472C /* ContactTrickEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickEntry.swift; sourceTree = "<group>"; };
-		E592A3712CEEC038009A472C /* ContactTrickRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickRootView.swift; sourceTree = "<group>"; };
-		E592A3732CEEC038009A472C /* ContactTrickDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickDataFlow.swift; sourceTree = "<group>"; };
-		E592A3742CEEC038009A472C /* ContactTrickProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickProvider.swift; sourceTree = "<group>"; };
-		E592A3752CEEC038009A472C /* ContactTrickStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTrickStateModel.swift; sourceTree = "<group>"; };
+		E592A3712CEEC038009A472C /* ContactImageRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageRootView.swift; sourceTree = "<group>"; };
+		E592A3732CEEC038009A472C /* ContactImageDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageDataFlow.swift; sourceTree = "<group>"; };
+		E592A3742CEEC038009A472C /* ContactImageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageProvider.swift; sourceTree = "<group>"; };
+		E592A3752CEEC038009A472C /* ContactImageStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageStateModel.swift; sourceTree = "<group>"; };
 		E625985B47742D498CB1681A /* GlucoseNotificationSettingsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GlucoseNotificationSettingsProvider.swift; sourceTree = "<group>"; };
 		F816825D28DB441200054060 /* HeartBeatManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeartBeatManager.swift; sourceTree = "<group>"; };
 		F816825F28DB441800054060 /* BluetoothTransmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothTransmitter.swift; sourceTree = "<group>"; };
@@ -1560,7 +1560,7 @@
 				E42231DBF0DBE2B4B92D1B15 /* CarbRatioEditor */,
 				F75CB57ED6971B46F8756083 /* CGM */,
 				0610F7D6D2EC00E3BA1569F0 /* ConfigEditor */,
-				E592A3762CEEC038009A472C /* ContactTrick */,
+				E592A3762CEEC038009A472C /* ContactImage */,
 				9E56E3626FAD933385101B76 /* DataTable */,
 				195D80B22AF696EE00D25097 /* DynamicSettings */,
 				DD17454C2C55CA0200211FAC /* GeneralSettings */,
@@ -1700,7 +1700,7 @@
 				3811DE9225C9D88200A708ED /* Appearance */,
 				CEB434E128B8F9BC00B70274 /* Bluetooth */,
 				3862CC2C2743F9DC00BF832C /* Calendar */,
-				E592A37E2CEEC046009A472C /* ContactTrick */,
+				E592A37E2CEEC046009A472C /* ContactImage */,
 				F90692A8274B7A980037068D /* HealthKit */,
 				6B1A8D2C2B156EC100E76752 /* LiveActivity */,
 				3811DE9425C9D88200A708ED /* Network */,
@@ -2083,15 +2083,15 @@
 		38A0362725ECF05300FCBB52 /* Storage */ = {
 			isa = PBXGroup;
 			children = (
-				DDB37CC42D05048F00D99BF4 /* ContactTrickStorage.swift */,
+				CE82E02428E867BA00473A9C /* AlertStorage.swift */,
 				385CEAC325F2F154002D6D5B /* AnnouncementsStorage.swift */,
 				38AEE75625F0F18E0013F05B /* CarbsStorage.swift */,
+				DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */,
+				5864E8582C42CFAE00294306 /* DeterminationStorage.swift */,
 				38A0363A25ECF07E00FCBB52 /* GlucoseStorage.swift */,
+				BDC2EA442C3043B000E5BBD0 /* OverrideStorage.swift */,
 				38FCF3FC25E997A80078B0D1 /* PumpHistoryStorage.swift */,
 				38F3B2EE25ED8E2A005C48AA /* TempTargetsStorage.swift */,
-				CE82E02428E867BA00473A9C /* AlertStorage.swift */,
-				BDC2EA442C3043B000E5BBD0 /* OverrideStorage.swift */,
-				5864E8582C42CFAE00294306 /* DeterminationStorage.swift */,
 			);
 			path = Storage;
 			sourceTree = "<group>";
@@ -2994,32 +2994,32 @@
 		E592A3722CEEC038009A472C /* View */ = {
 			isa = PBXGroup;
 			children = (
-				E592A3712CEEC038009A472C /* ContactTrickRootView.swift */,
-				BDC531112D1060FA00088832 /* ContactTrickDetailView.swift */,
-				BDC531132D10611D00088832 /* AddContactTrickSheet.swift */,
+				E592A3712CEEC038009A472C /* ContactImageRootView.swift */,
+				BDC531112D1060FA00088832 /* ContactImageDetailView.swift */,
+				BDC531132D10611D00088832 /* AddContactImageSheet.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
 		};
-		E592A3762CEEC038009A472C /* ContactTrick */ = {
+		E592A3762CEEC038009A472C /* ContactImage */ = {
 			isa = PBXGroup;
 			children = (
 				E592A3722CEEC038009A472C /* View */,
-				E592A3732CEEC038009A472C /* ContactTrickDataFlow.swift */,
-				E592A3742CEEC038009A472C /* ContactTrickProvider.swift */,
-				E592A3752CEEC038009A472C /* ContactTrickStateModel.swift */,
+				E592A3732CEEC038009A472C /* ContactImageDataFlow.swift */,
+				E592A3742CEEC038009A472C /* ContactImageProvider.swift */,
+				E592A3752CEEC038009A472C /* ContactImageStateModel.swift */,
 			);
-			path = ContactTrick;
+			path = ContactImage;
 			sourceTree = "<group>";
 		};
-		E592A37E2CEEC046009A472C /* ContactTrick */ = {
+		E592A37E2CEEC046009A472C /* ContactImage */ = {
 			isa = PBXGroup;
 			children = (
-				BDC530FE2D0F6BE300088832 /* ContactTrickManager.swift */,
-				BDC531152D10629000088832 /* ContactTrickPicture.swift */,
-				BDC531172D1062F200088832 /* ContactTrickState.swift */,
+				BDC530FE2D0F6BE300088832 /* ContactImageManager.swift */,
+				BDC531152D10629000088832 /* ContactPicture.swift */,
+				BDC531172D1062F200088832 /* ContactImageState.swift */,
 			);
-			path = ContactTrick;
+			path = ContactImage;
 			sourceTree = "<group>";
 		};
 		EEC747824D6593B5CD87E195 /* View */ = {
@@ -3725,7 +3725,7 @@
 				6632A0DC746872439A858B44 /* ISFEditorDataFlow.swift in Sources */,
 				DD17454B2C55C62800211FAC /* AutosensSettingsRootView.swift in Sources */,
 				DDF847DF2C5C28780049BB3B /* LiveActivitySettingsProvider.swift in Sources */,
-				DDB37CC52D05048F00D99BF4 /* ContactTrickStorage.swift in Sources */,
+				DDB37CC52D05048F00D99BF4 /* ContactImageStorage.swift in Sources */,
 				DBA5254DBB2586C98F61220C /* ISFEditorProvider.swift in Sources */,
 				BDF34EBE2C0A31D100D51995 /* CustomNotification.swift in Sources */,
 				BDC2EA472C3045AD00E5BBD0 /* Override.swift in Sources */,
@@ -3778,11 +3778,11 @@
 				DD5DC9F92CF3DAA900AB8703 /* RadioButton.swift in Sources */,
 				38E44522274E3DDC00EC9A94 /* NetworkReachabilityManager.swift in Sources */,
 				CE7CA34F2A064973004BE681 /* BaseIntentsRequest.swift in Sources */,
-				E592A3772CEEC038009A472C /* ContactTrickStateModel.swift in Sources */,
-				E592A3782CEEC038009A472C /* ContactTrickDataFlow.swift in Sources */,
-				E592A3792CEEC038009A472C /* ContactTrickRootView.swift in Sources */,
-				BDC531182D1062F200088832 /* ContactTrickState.swift in Sources */,
-				E592A37A2CEEC038009A472C /* ContactTrickProvider.swift in Sources */,
+				E592A3772CEEC038009A472C /* ContactImageStateModel.swift in Sources */,
+				E592A3782CEEC038009A472C /* ContactImageDataFlow.swift in Sources */,
+				E592A3792CEEC038009A472C /* ContactImageRootView.swift in Sources */,
+				BDC531182D1062F200088832 /* ContactImageState.swift in Sources */,
+				E592A37A2CEEC038009A472C /* ContactImageProvider.swift in Sources */,
 				CE82E02728E869DF00473A9C /* AlertEntry.swift in Sources */,
 				38E4451E274DB04600EC9A94 /* AppDelegate.swift in Sources */,
 				BD2FF1A02AE29D43005D1C5D /* CheckboxToggleStyle.swift in Sources */,
@@ -3801,7 +3801,7 @@
 				69A31254F2451C20361D172F /* TreatmentsStateModel.swift in Sources */,
 				1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */,
 				19D4E4EB29FC6A9F00351451 /* Charts.swift in Sources */,
-				BDC531162D10629000088832 /* ContactTrickPicture.swift in Sources */,
+				BDC531162D10629000088832 /* ContactPicture.swift in Sources */,
 				FEFFA7A22929FE49007B8193 /* UIDevice+Extensions.swift in Sources */,
 				F90692D3274B9A130037068D /* AppleHealthKitRootView.swift in Sources */,
 				BDF34F852C10C62E00D51995 /* GlucoseData.swift in Sources */,
@@ -3859,8 +3859,8 @@
 				DDE179522C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift in Sources */,
 				DDE179532C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift in Sources */,
 				DDE179542C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift in Sources */,
-				BDC530FF2D0F6BE300088832 /* ContactTrickManager.swift in Sources */,
-				BDC531122D1060FA00088832 /* ContactTrickDetailView.swift in Sources */,
+				BDC530FF2D0F6BE300088832 /* ContactImageManager.swift in Sources */,
+				BDC531122D1060FA00088832 /* ContactImageDetailView.swift in Sources */,
 				DDE179552C910127003CDDB7 /* LoopStatRecord+CoreDataProperties.swift in Sources */,
 				DDE179562C910127003CDDB7 /* BolusStored+CoreDataClass.swift in Sources */,
 				DDE179572C910127003CDDB7 /* BolusStored+CoreDataProperties.swift in Sources */,
@@ -3877,7 +3877,7 @@
 				DDE179632C910127003CDDB7 /* Forecast+CoreDataProperties.swift in Sources */,
 				DDE179642C910127003CDDB7 /* GlucoseStored+CoreDataClass.swift in Sources */,
 				DDE179652C910127003CDDB7 /* GlucoseStored+CoreDataProperties.swift in Sources */,
-				BDC531142D10611D00088832 /* AddContactTrickSheet.swift in Sources */,
+				BDC531142D10611D00088832 /* AddContactImageSheet.swift in Sources */,
 				DDE179662C910127003CDDB7 /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
 				DDE179672C910127003CDDB7 /* OpenAPS_Battery+CoreDataProperties.swift in Sources */,
 				DDE179682C910127003CDDB7 /* TempBasalStored+CoreDataClass.swift in Sources */,

+ 0 - 1
FreeAPS/Sources/APS/OpenAPS/Constants.swift

@@ -39,7 +39,6 @@ extension OpenAPS {
         static let carbRatios = "settings/carb_ratios.json"
         static let tempTargets = "settings/temptargets.json"
         static let model = "settings/model.json"
-        static let contactTrick = "settings/contact_trick.json"
     }
 
     enum Monitor {

+ 150 - 0
FreeAPS/Sources/APS/Storage/ContactImageStorage.swift

@@ -0,0 +1,150 @@
+import CoreData
+import Foundation
+import SwiftUI
+import Swinject
+
+protocol ContactImageStorage {
+    func fetchContactImageEntries() async -> [ContactImageEntry]
+    func storeContactImageEntry(_ entry: ContactImageEntry) async
+    func updateContactImageEntry(_ contactImageEntry: ContactImageEntry) async
+    func deleteContactImageEntry(_ objectID: NSManagedObjectID) async
+}
+
+final class BaseContactImageStorage: ContactImageStorage, Injectable {
+    @Injected() private var settingsManager: SettingsManager!
+
+    private let backgroundContext = CoreDataStack.shared.newTaskContext()
+
+    init(resolver: Resolver) {
+        injectServices(resolver)
+    }
+
+    /// Fetches all stored Contact Trick entries.
+    ///
+    /// The method retrieves `ContactImageEntryStored` objects from Core Data, maps them to
+    /// `ContactImageEntry` objects, and returns the results.
+    ///
+    /// - Returns: An array of `ContactImageEntry` objects.
+    func fetchContactImageEntries() async -> [ContactImageEntry] {
+        let results = await CoreDataStack.shared.fetchEntitiesAsync(
+            ofType: ContactImageEntryStored.self,
+            onContext: backgroundContext,
+            predicate: NSPredicate.all,
+            key: "hasHighContrast",
+            ascending: false
+        )
+
+        return await backgroundContext.perform {
+            guard let fetchedContactImageEntries = results as? [ContactImageEntryStored] else { return [] }
+
+            return fetchedContactImageEntries.compactMap { entry in
+                ContactImageEntry(
+                    name: entry.name ?? "No name provided",
+                    layout: ContactImageLayout(rawValue: entry.layout ?? "Default") ?? .default,
+                    ring: ContactImageLargeRing(rawValue: entry.ring ?? "Hidden") ?? .none,
+                    primary: ContactImageValue(rawValue: entry.primary ?? "Glucose Reading") ?? .glucose,
+                    top: ContactImageValue(rawValue: entry.top ?? "None") ?? .none,
+                    bottom: ContactImageValue(rawValue: entry.bottom ?? "None") ?? .none,
+                    contactId: entry.contactId?.string,
+                    hasHighContrast: entry.hasHighContrast,
+                    ringWidth: ContactImageEntry.RingWidth(rawValue: Int(entry.ringWidth)) ?? .regular,
+                    ringGap: ContactImageEntry.RingGap(rawValue: Int(entry.ringGap)) ?? .small,
+                    fontSize: ContactImageEntry.FontSize(rawValue: Int(entry.fontSize)) ?? .regular,
+                    secondaryFontSize: ContactImageEntry.FontSize(rawValue: Int(entry.fontSizeSecondary)) ?? .small,
+                    fontWeight: Font.Weight.fromString(entry.fontWeight ?? "regular"),
+                    fontWidth: Font.Width.fromString(entry.fontWidth ?? "standard"),
+                    managedObjectID: entry.objectID
+                )
+            }
+        }
+    }
+
+    /// Stores a new Contact Trick entry.
+    ///
+    /// This method creates a new `ContactImageEntryStored` object in the background context,
+    /// populates its properties with the values from the provided `ContactImageEntry`, and
+    /// saves the context if changes exist.
+    ///
+    /// - Parameter contactImageEntry: The `ContactImageEntry` object to be stored.
+    func storeContactImageEntry(_ contactImageEntry: ContactImageEntry) async {
+        await backgroundContext.perform {
+            let newContactImageEntry = ContactImageEntryStored(context: self.backgroundContext)
+
+            newContactImageEntry.id = UUID()
+            newContactImageEntry.name = contactImageEntry.name
+            newContactImageEntry.contactId = contactImageEntry.contactId
+            newContactImageEntry.layout = contactImageEntry.layout.rawValue
+            newContactImageEntry.ring = contactImageEntry.ring.rawValue
+            newContactImageEntry.primary = contactImageEntry.primary.rawValue
+            newContactImageEntry.top = contactImageEntry.top.rawValue
+            newContactImageEntry.bottom = contactImageEntry.bottom.rawValue
+            newContactImageEntry.hasHighContrast = contactImageEntry.hasHighContrast
+            newContactImageEntry.ringWidth = Int16(contactImageEntry.ringWidth.rawValue)
+            newContactImageEntry.ringGap = Int16(contactImageEntry.ringGap.rawValue)
+            newContactImageEntry.fontSize = Int16(contactImageEntry.fontSize.rawValue)
+            newContactImageEntry.fontSizeSecondary = Int16(contactImageEntry.secondaryFontSize.rawValue)
+            newContactImageEntry.fontWidth = contactImageEntry.fontWeight.asString
+            newContactImageEntry.fontWeight = contactImageEntry.fontWidth.asString
+
+            do {
+                guard self.backgroundContext.hasChanges else { return }
+                try self.backgroundContext.save()
+            } catch let error as NSError {
+                debugPrint(
+                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to save Contact Trick Entry to Core Data with error: \(error.userInfo)"
+                )
+            }
+        }
+    }
+
+    /// Updates an existing Contact Trick entry in Core Data.
+    ///
+    /// This method finds the existing `ContactImageEntryStored` object by its `contactId` and updates
+    /// its properties with the values from the provided `ContactImageEntry`. If no matching entry exists,
+    /// it does nothing.
+    ///
+    /// - Parameter contactImageEntry: The `ContactImageEntry` object with updated values.
+    func updateContactImageEntry(_ contactImageEntry: ContactImageEntry) async {
+        await backgroundContext.perform {
+            let fetchRequest: NSFetchRequest<ContactImageEntryStored> = ContactImageEntryStored.fetchRequest()
+            fetchRequest.predicate = NSPredicate(format: "contactId == %@", contactImageEntry.contactId ?? "")
+
+            do {
+                if let existingEntry = try self.backgroundContext.fetch(fetchRequest).first {
+                    // Update the properties of the existing entry
+                    existingEntry.name = contactImageEntry.name
+                    existingEntry.layout = contactImageEntry.layout.rawValue
+                    existingEntry.ring = contactImageEntry.ring.rawValue
+                    existingEntry.primary = contactImageEntry.primary.rawValue
+                    existingEntry.top = contactImageEntry.top.rawValue
+                    existingEntry.bottom = contactImageEntry.bottom.rawValue
+                    existingEntry.hasHighContrast = contactImageEntry.hasHighContrast
+                    existingEntry.ringWidth = Int16(contactImageEntry.ringWidth.rawValue)
+                    existingEntry.ringGap = Int16(contactImageEntry.ringGap.rawValue)
+                    existingEntry.fontSize = Int16(contactImageEntry.fontSize.rawValue)
+                    existingEntry.fontSizeSecondary = Int16(contactImageEntry.secondaryFontSize.rawValue)
+                    existingEntry.fontWeight = contactImageEntry.fontWeight.asString
+                    existingEntry.fontWidth = contactImageEntry.fontWidth.asString
+
+                    guard self.backgroundContext.hasChanges else { return }
+                    try self.backgroundContext.save()
+                } else {
+                    debugPrint(
+                        "\(DebuggingIdentifiers.failed) \(#file) \(#function) No matching Contact Trick Entry found to update."
+                    )
+                }
+            } catch let error as NSError {
+                debugPrint(
+                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to update Contact Trick Entry with error: \(error.userInfo)"
+                )
+            }
+        }
+    }
+
+    /// Deletes a Contact Trick entry from Core Data.
+    ///
+    /// - Parameter objectID: The `NSManagedObjectID` of the object to delete.
+    func deleteContactImageEntry(_ objectID: NSManagedObjectID) async {
+        await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
+    }
+}

+ 0 - 150
FreeAPS/Sources/APS/Storage/ContactTrickStorage.swift

@@ -1,150 +0,0 @@
-import CoreData
-import Foundation
-import SwiftUI
-import Swinject
-
-protocol ContactTrickStorage {
-    func fetchContactTrickEntries() async -> [ContactTrickEntry]
-    func storeContactTrickEntry(_ entry: ContactTrickEntry) async
-    func updateContactTrickEntry(_ contactTrickEntry: ContactTrickEntry) async
-    func deleteContactTrickEntry(_ objectID: NSManagedObjectID) async
-}
-
-final class BaseContactTrickStorage: ContactTrickStorage, Injectable {
-    @Injected() private var settingsManager: SettingsManager!
-
-    private let backgroundContext = CoreDataStack.shared.newTaskContext()
-
-    init(resolver: Resolver) {
-        injectServices(resolver)
-    }
-
-    /// Fetches all stored Contact Trick entries.
-    ///
-    /// The method retrieves `ContactTrickEntryStored` objects from Core Data, maps them to
-    /// `ContactTrickEntry` objects, and returns the results.
-    ///
-    /// - Returns: An array of `ContactTrickEntry` objects.
-    func fetchContactTrickEntries() async -> [ContactTrickEntry] {
-        let results = await CoreDataStack.shared.fetchEntitiesAsync(
-            ofType: ContactTrickEntryStored.self,
-            onContext: backgroundContext,
-            predicate: NSPredicate.all,
-            key: "hasHighContrast",
-            ascending: false
-        )
-
-        return await backgroundContext.perform {
-            guard let fetchedContactTrickEntries = results as? [ContactTrickEntryStored] else { return [] }
-
-            return fetchedContactTrickEntries.compactMap { entry in
-                ContactTrickEntry(
-                    name: entry.name ?? "No name provided",
-                    layout: ContactTrickLayout(rawValue: entry.layout ?? "Single") ?? .single,
-                    ring: ContactTrickLargeRing(rawValue: entry.ring ?? "Hidden") ?? .none,
-                    primary: ContactTrickValue(rawValue: entry.primary ?? "Glucose Reading") ?? .glucose,
-                    top: ContactTrickValue(rawValue: entry.top ?? "None") ?? .none,
-                    bottom: ContactTrickValue(rawValue: entry.bottom ?? "None") ?? .none,
-                    contactId: entry.contactId?.string,
-                    hasHighContrast: entry.hasHighContrast,
-                    ringWidth: ContactTrickEntry.RingWidth(rawValue: Int(entry.ringWidth)) ?? .regular,
-                    ringGap: ContactTrickEntry.RingGap(rawValue: Int(entry.ringGap)) ?? .small,
-                    fontSize: ContactTrickEntry.FontSize(rawValue: Int(entry.fontSize)) ?? .regular,
-                    secondaryFontSize: ContactTrickEntry.FontSize(rawValue: Int(entry.fontSizeSecondary)) ?? .small,
-                    fontWeight: Font.Weight.fromString(entry.fontWeight ?? "regular"),
-                    fontWidth: Font.Width.fromString(entry.fontWidth ?? "standard"),
-                    managedObjectID: entry.objectID
-                )
-            }
-        }
-    }
-
-    /// Stores a new Contact Trick entry.
-    ///
-    /// This method creates a new `ContactTrickEntryStored` object in the background context,
-    /// populates its properties with the values from the provided `ContactTrickEntry`, and
-    /// saves the context if changes exist.
-    ///
-    /// - Parameter contactTrickEntry: The `ContactTrickEntry` object to be stored.
-    func storeContactTrickEntry(_ contactTrickEntry: ContactTrickEntry) async {
-        await backgroundContext.perform {
-            let newContactTrickEntry = ContactTrickEntryStored(context: self.backgroundContext)
-
-            newContactTrickEntry.id = UUID()
-            newContactTrickEntry.name = contactTrickEntry.name
-            newContactTrickEntry.contactId = contactTrickEntry.contactId
-            newContactTrickEntry.layout = contactTrickEntry.layout.rawValue
-            newContactTrickEntry.ring = contactTrickEntry.ring.rawValue
-            newContactTrickEntry.primary = contactTrickEntry.primary.rawValue
-            newContactTrickEntry.top = contactTrickEntry.top.rawValue
-            newContactTrickEntry.bottom = contactTrickEntry.bottom.rawValue
-            newContactTrickEntry.hasHighContrast = contactTrickEntry.hasHighContrast
-            newContactTrickEntry.ringWidth = Int16(contactTrickEntry.ringWidth.rawValue)
-            newContactTrickEntry.ringGap = Int16(contactTrickEntry.ringGap.rawValue)
-            newContactTrickEntry.fontSize = Int16(contactTrickEntry.fontSize.rawValue)
-            newContactTrickEntry.fontSizeSecondary = Int16(contactTrickEntry.secondaryFontSize.rawValue)
-            newContactTrickEntry.fontWidth = contactTrickEntry.fontWeight.asString
-            newContactTrickEntry.fontWeight = contactTrickEntry.fontWidth.asString
-
-            do {
-                guard self.backgroundContext.hasChanges else { return }
-                try self.backgroundContext.save()
-            } catch let error as NSError {
-                debugPrint(
-                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to save Contact Trick Entry to Core Data with error: \(error.userInfo)"
-                )
-            }
-        }
-    }
-
-    /// Updates an existing Contact Trick entry in Core Data.
-    ///
-    /// This method finds the existing `ContactTrickEntryStored` object by its `contactId` and updates
-    /// its properties with the values from the provided `ContactTrickEntry`. If no matching entry exists,
-    /// it does nothing.
-    ///
-    /// - Parameter contactTrickEntry: The `ContactTrickEntry` object with updated values.
-    func updateContactTrickEntry(_ contactTrickEntry: ContactTrickEntry) async {
-        await backgroundContext.perform {
-            let fetchRequest: NSFetchRequest<ContactTrickEntryStored> = ContactTrickEntryStored.fetchRequest()
-            fetchRequest.predicate = NSPredicate(format: "contactId == %@", contactTrickEntry.contactId ?? "")
-
-            do {
-                if let existingEntry = try self.backgroundContext.fetch(fetchRequest).first {
-                    // Update the properties of the existing entry
-                    existingEntry.name = contactTrickEntry.name
-                    existingEntry.layout = contactTrickEntry.layout.rawValue
-                    existingEntry.ring = contactTrickEntry.ring.rawValue
-                    existingEntry.primary = contactTrickEntry.primary.rawValue
-                    existingEntry.top = contactTrickEntry.top.rawValue
-                    existingEntry.bottom = contactTrickEntry.bottom.rawValue
-                    existingEntry.hasHighContrast = contactTrickEntry.hasHighContrast
-                    existingEntry.ringWidth = Int16(contactTrickEntry.ringWidth.rawValue)
-                    existingEntry.ringGap = Int16(contactTrickEntry.ringGap.rawValue)
-                    existingEntry.fontSize = Int16(contactTrickEntry.fontSize.rawValue)
-                    existingEntry.fontSizeSecondary = Int16(contactTrickEntry.secondaryFontSize.rawValue)
-                    existingEntry.fontWeight = contactTrickEntry.fontWeight.asString
-                    existingEntry.fontWidth = contactTrickEntry.fontWidth.asString
-
-                    guard self.backgroundContext.hasChanges else { return }
-                    try self.backgroundContext.save()
-                } else {
-                    debugPrint(
-                        "\(DebuggingIdentifiers.failed) \(#file) \(#function) No matching Contact Trick Entry found to update."
-                    )
-                }
-            } catch let error as NSError {
-                debugPrint(
-                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to update Contact Trick Entry with error: \(error.userInfo)"
-                )
-            }
-        }
-    }
-
-    /// Deletes a Contact Trick entry from Core Data.
-    ///
-    /// - Parameter objectID: The `NSManagedObjectID` of the object to delete.
-    func deleteContactTrickEntry(_ objectID: NSManagedObjectID) async {
-        await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
-    }
-}

+ 1 - 1
FreeAPS/Sources/Assemblies/ServiceAssembly.swift

@@ -20,7 +20,7 @@ final class ServiceAssembly: Assembly {
         container.register(UserNotificationsManager.self) { r in BaseUserNotificationsManager(resolver: r) }
         container.register(WatchManager.self) { r in BaseWatchManager(resolver: r) }
         container.register(GarminManager.self) { r in BaseGarminManager(resolver: r) }
-        container.register(ContactTrickManager.self) { r in BaseContactTrickManager(resolver: r) }
+        container.register(ContactImageManager.self) { r in BaseContactImageManager(resolver: r) }
         container.register(AlertPermissionsChecker.self) { r in AlertPermissionsChecker(resolver: r) }
         if #available(iOS 16.2, *) {
             container.register(LiveActivityBridge.self) { r in

+ 1 - 1
FreeAPS/Sources/Assemblies/StorageAssembly.swift

@@ -13,7 +13,7 @@ final class StorageAssembly: Assembly {
         container.register(GlucoseStorage.self) { r in BaseGlucoseStorage(resolver: r) }
         container.register(TempTargetsStorage.self) { r in BaseTempTargetsStorage(resolver: r) }
         container.register(CarbsStorage.self) { r in BaseCarbsStorage(resolver: r) }
-        container.register(ContactTrickStorage.self) { r in BaseContactTrickStorage(resolver: r) }
+        container.register(ContactImageStorage.self) { r in BaseContactImageStorage(resolver: r) }
         container.register(AnnouncementsStorage.self) { r in BaseAnnouncementsStorage(resolver: r) }
         container.register(SettingsManager.self) { r in BaseSettingsManager(resolver: r) }
         container.register(Keychain.self) { _ in BaseKeychain() }

+ 15 - 15
FreeAPS/Sources/Models/ContactTrickEntry.swift

@@ -1,14 +1,14 @@
 import CoreData
 import SwiftUI
 
-struct ContactTrickEntry: Hashable, Equatable, Sendable {
+struct ContactImageEntry: Hashable, Equatable, Sendable {
     var id = UUID()
     var name: String = ""
-    var layout: ContactTrickLayout = .single
-    var ring: ContactTrickLargeRing = .none
-    var primary: ContactTrickValue = .glucose
-    var top: ContactTrickValue = .none
-    var bottom: ContactTrickValue = .none
+    var layout: ContactImageLayout = .default
+    var ring: ContactImageLargeRing = .none
+    var primary: ContactImageValue = .glucose
+    var top: ContactImageValue = .none
+    var bottom: ContactImageValue = .none
     var contactId: String? = nil
     var hasHighContrast: Bool = true
     var ringWidth: RingWidth = .regular
@@ -19,7 +19,7 @@ struct ContactTrickEntry: Hashable, Equatable, Sendable {
     var fontWidth: Font.Width = .standard
     var managedObjectID: NSManagedObjectID?
 
-    static func == (lhs: ContactTrickEntry, rhs: ContactTrickEntry) -> Bool {
+    static func == (lhs: ContactImageEntry, rhs: ContactImageEntry) -> Bool {
         lhs.id == rhs.id &&
             lhs.name == rhs.name &&
             lhs.layout == rhs.layout &&
@@ -110,12 +110,12 @@ struct ContactTrickEntry: Hashable, Equatable, Sendable {
     }
 }
 
-protocol ContactTrickObserver: Sendable {
+protocol ContactImageObserver: Sendable {
     // TODO: is this required?
-//    func basalProfileDidChange(_ entry: [ContactTrickEntry])
+//    func basalProfileDidChange(_ entry: [ContactImageEntry])
 }
 
-enum ContactTrickValue: String, JSON, CaseIterable, Identifiable, Codable {
+enum ContactImageValue: String, JSON, CaseIterable, Identifiable, Codable {
     var id: String { rawValue }
     case none
     case glucose
@@ -151,22 +151,22 @@ enum ContactTrickValue: String, JSON, CaseIterable, Identifiable, Codable {
     }
 }
 
-enum ContactTrickLayout: String, JSON, CaseIterable, Identifiable, Codable {
+enum ContactImageLayout: String, JSON, CaseIterable, Identifiable, Codable {
     var id: String { rawValue }
-    case single
+    case `default`
     case split
 
     var displayName: String {
         switch self {
-        case .single:
-            return NSLocalizedString("Single", comment: "")
+        case .default:
+            return NSLocalizedString("Default", comment: "")
         case .split:
             return NSLocalizedString("Split", comment: "")
         }
     }
 }
 
-enum ContactTrickLargeRing: String, JSON, CaseIterable, Identifiable, Codable {
+enum ContactImageLargeRing: String, JSON, CaseIterable, Identifiable, Codable {
     // TODO: revisit rings for iob, cob and combined iob+cob with more user feedback
     var id: String { rawValue }
     case none

+ 8 - 0
FreeAPS/Sources/Modules/ContactImage/ContactImageDataFlow.swift

@@ -0,0 +1,8 @@
+import Combine
+import Foundation
+
+enum ContactImage {
+    enum Config {}
+}
+
+protocol ContactImageProvider: Provider {}

+ 6 - 0
FreeAPS/Sources/Modules/ContactImage/ContactImageProvider.swift

@@ -0,0 +1,6 @@
+import Combine
+import Foundation
+
+extension ContactImage {
+    final class Provider: BaseProvider, ContactImageProvider {}
+}

+ 49 - 49
FreeAPS/Sources/Modules/ContactTrick/ContactTrickStateModel.swift

@@ -2,69 +2,69 @@ import ConnectIQ
 import CoreData
 import SwiftUI
 
-extension ContactTrick {
-    @Observable final class StateModel: BaseStateModel<Provider>, ContactTrickManagerDelegate {
-        @ObservationIgnored @Injected() var contactTrickStorage: ContactTrickStorage!
-        @ObservationIgnored @Injected() var contactTrickManager: ContactTrickManager!
+extension ContactImage {
+    @Observable final class StateModel: BaseStateModel<Provider>, ContactImageManagerDelegate {
+        @ObservationIgnored @Injected() var contactImageStorage: ContactImageStorage!
+        @ObservationIgnored @Injected() var contactImageManager: ContactImageManager!
 
-        var contactTrickEntries = [ContactTrickEntry]()
+        var contactImageEntries = [ContactImageEntry]()
         var units: GlucoseUnits = .mmolL
         // Help Sheet
         var isHelpSheetPresented: Bool = false
         var helpSheetDetent = PresentationDetent.large
 
         // Current state for live preview
-        var state = ContactTrickState()
+        var state = ContactImageState()
 
         /// Subscribes to updates and initializes data fetching.
         override func subscribe() {
             units = settingsManager.settings.units
-            contactTrickManager.delegate = self
+            contactImageManager.delegate = self
 
             Task {
-                /// Initial fetch to fill the ContactTrickEntry array
-                await fetchContactTrickEntriesAndUpdateUI()
+                /// Initial fetch to fill the ContactImageEntry array
+                await fetchContactImageEntriesAndUpdateUI()
 
                 // Initial state update is needed for preview
-                await contactTrickManager.updateContactTrickState()
+                await contactImageManager.updateContactImageState()
             }
         }
 
-        func contactTrickManagerDidUpdateState(_ state: ContactTrickState) {
+        func contactImageManagerDidUpdateState(_ state: ContactImageState) {
             Task { @MainActor in
                 self.state = state
             }
         }
 
-        /// Fetches all ContactTrickEntries and validates them against iOS Contacts.
-        func fetchContactTrickEntriesAndUpdateUI() async {
+        /// Fetches all ContactImageEntries and validates them against iOS Contacts.
+        func fetchContactImageEntriesAndUpdateUI() async {
             // 1. Get all entries from Core Data
-            let cdEntries = await contactTrickStorage.fetchContactTrickEntries()
+            let cdEntries = await contactImageStorage.fetchContactImageEntries()
 
             // 2. Validate entries against iOS Contacts
             let validatedEntries = await validateEntries(cdEntries)
 
             // 3. Update UI with validated entries
             await MainActor.run {
-                self.contactTrickEntries = validatedEntries
+                self.contactImageEntries = validatedEntries
             }
         }
 
         /// Validates entries against iOS Contacts and removes invalid ones
-        private func validateEntries(_ entries: [ContactTrickEntry]) async -> [ContactTrickEntry] {
-            var validated: [ContactTrickEntry] = []
+        private func validateEntries(_ entries: [ContactImageEntry]) async -> [ContactImageEntry] {
+            var validated: [ContactImageEntry] = []
 
             for entry in entries {
                 if let contactId = entry.contactId {
                     // Check if contact still exists in iOS Contacts
-                    let exists = await contactTrickManager.validateContactExists(withIdentifier: contactId)
+                    let exists = await contactImageManager.validateContactExists(withIdentifier: contactId)
 
                     if exists {
                         validated.append(entry)
                     } else {
                         // Contact was deleted in iOS, remove from Core Data
                         if let objectID = entry.managedObjectID {
-                            await contactTrickStorage.deleteContactTrickEntry(objectID)
+                            await contactImageStorage.deleteContactImageEntry(objectID)
                             debugPrint("Removed orphaned contact entry: \(entry.name)")
                         }
                     }
@@ -76,18 +76,18 @@ extension ContactTrick {
 
         /// Creates a new contact in Apple Contacts and saves it to Core Data.
         /// - Parameters:
-        ///   - entry: The ContactTrickEntry to be saved.
+        ///   - entry: The ContactImageEntry to be saved.
         ///   - name: The name of the contact.
-        func createAndSaveContactTrick(entry: ContactTrickEntry, name: String) async {
+        func createAndSaveContactImage(entry: ContactImageEntry, name: String) async {
             // 1. Check for contact access permissions.
-            let hasAccess = await contactTrickManager.requestAccess()
+            let hasAccess = await contactImageManager.requestAccess()
             guard hasAccess else {
                 debugPrint("\(DebuggingIdentifiers.failed) No access to contacts.")
                 return
             }
 
             // 2. Create the contact and retrieve its `identifier`.
-            guard let contactId = await contactTrickManager.createContact(name: name) else {
+            guard let contactId = await contactImageManager.createContact(name: name) else {
                 debugPrint("\(DebuggingIdentifiers.failed) Failed to create contact.")
                 return
             }
@@ -98,30 +98,30 @@ extension ContactTrick {
             updatedEntry.name = name
 
             // 4. Save the contact to Core Data.
-            await addContactTrickEntry(updatedEntry)
+            await addContactImageEntry(updatedEntry)
 
-            // 5. Update ContactTrickState and set the image for the newly created contact
-            await contactTrickManager.updateContactTrickState()
-            await contactTrickManager.setImageForContact(contactId: contactId)
+            // 5. Update ContactImageState and set the image for the newly created contact
+            await contactImageManager.updateContactImageState()
+            await contactImageManager.setImageForContact(contactId: contactId)
         }
 
-        /// Adds a ContactTrickEntry to Core Data.
-        /// - Parameter entry: The ContactTrickEntry to be saved.
-        func addContactTrickEntry(_ entry: ContactTrickEntry) async {
-            await contactTrickStorage.storeContactTrickEntry(entry)
-            await fetchContactTrickEntriesAndUpdateUI()
+        /// Adds a ContactImageEntry to Core Data.
+        /// - Parameter entry: The ContactImageEntry to be saved.
+        func addContactImageEntry(_ entry: ContactImageEntry) async {
+            await contactImageStorage.storeContactImageEntry(entry)
+            await fetchContactImageEntriesAndUpdateUI()
         }
 
         /// Deletes a contact from Apple Contacts and Core Data.
-        /// - Parameter entry: The ContactTrickEntry representing the contact to be deleted.
-        func deleteContact(entry: ContactTrickEntry) async {
+        /// - Parameter entry: The ContactImageEntry representing the contact to be deleted.
+        func deleteContact(entry: ContactImageEntry) async {
             guard let contactId = entry.contactId else {
                 debugPrint("\(DebuggingIdentifiers.failed) Contact does not have a valid ID.")
                 return
             }
 
             // 1. Attempt to delete the contact from Apple Contacts.
-            let contactDeleted = await contactTrickManager.deleteContact(withIdentifier: contactId)
+            let contactDeleted = await contactImageManager.deleteContact(withIdentifier: contactId)
             if contactDeleted {
                 debugPrint("\(DebuggingIdentifiers.succeeded) Contact successfully deleted from Apple Contacts: \(contactId)")
             } else {
@@ -130,33 +130,33 @@ extension ContactTrick {
 
             // 2. Delete the entry from Core Data.
             if let objectID = entry.managedObjectID {
-                await deleteContactTrick(objectID: objectID)
+                await deleteContactImage(objectID: objectID)
             }
         }
 
         /// Deletes a Core Data entry.
         /// - Parameter objectID: The Managed Object ID of the entry to be deleted.
-        func deleteContactTrick(objectID: NSManagedObjectID) async {
-            await contactTrickStorage.deleteContactTrickEntry(objectID)
-            await fetchContactTrickEntriesAndUpdateUI()
+        func deleteContactImage(objectID: NSManagedObjectID) async {
+            await contactImageStorage.deleteContactImageEntry(objectID)
+            await fetchContactImageEntriesAndUpdateUI()
         }
 
         /// Updates a contact in Apple Contacts and Core Data.
         /// - Parameters:
-        ///   - entry: The ContactTrickEntry to be updated.
-        func updateContact(with entry: ContactTrickEntry) async {
+        ///   - entry: The ContactImageEntry to be updated.
+        func updateContact(with entry: ContactImageEntry) async {
             guard let contactId = entry.contactId else {
                 debugPrint("\(DebuggingIdentifiers.failed) Contact does not have a valid ID.")
                 return
             }
 
             // 1. Update the entry in Core Data.
-            await updateContactTrick(entry)
+            await updateContactImage(entry)
 
             // 2. Update the contact in Apple Contacts.
 
             /// Update name
-            let contactUpdated = await contactTrickManager
+            let contactUpdated = await contactImageManager
                 .updateContact(withIdentifier: contactId, newName: entry.name) // TODO: - Probably not needed anymore
 
             guard contactUpdated else {
@@ -165,15 +165,15 @@ extension ContactTrick {
             }
 
             /// Update state and image
-            await contactTrickManager.updateContactTrickState()
-            await contactTrickManager.setImageForContact(contactId: contactId)
+            await contactImageManager.updateContactImageState()
+            await contactImageManager.setImageForContact(contactId: contactId)
         }
 
         /// Updates a Core Data entry.
-        /// - Parameter entry: The updated ContactTrickEntry.
-        func updateContactTrick(_ entry: ContactTrickEntry) async {
-            await contactTrickStorage.updateContactTrickEntry(entry)
-            await fetchContactTrickEntriesAndUpdateUI()
+        /// - Parameter entry: The updated ContactImageEntry.
+        func updateContactImage(_ entry: ContactImageEntry) async {
+            await contactImageStorage.updateContactImageEntry(entry)
+            await fetchContactImageEntriesAndUpdateUI()
         }
     }
 }

+ 42 - 35
FreeAPS/Sources/Modules/ContactTrick/View/AddContactTrickSheet.swift

@@ -1,27 +1,27 @@
 import SwiftUI
 
-struct AddContactTrickSheet: View {
+struct AddContactImageSheet: View {
     @Environment(\.dismiss) var dismiss
     @Environment(\.colorScheme) var colorScheme
     @Environment(AppState.self) var appState
 
-    @ObservedObject var state: ContactTrick.StateModel
+    @ObservedObject var state: ContactImage.StateModel
 
     @State private var hasHighContrast: Bool = true
-    @State private var ringWidth: ContactTrickEntry.RingWidth = .regular
-    @State private var ringGap: ContactTrickEntry.RingGap = .small
-    @State private var layout: ContactTrickLayout = .single
-    @State private var primary: ContactTrickValue = .glucose
-    @State private var top: ContactTrickValue = .none
-    @State private var bottom: ContactTrickValue = .trend
-    @State private var ring: ContactTrickLargeRing = .none
-    @State private var fontSize: ContactTrickEntry.FontSize = .regular
-    @State private var secondaryFontSize: ContactTrickEntry.FontSize = .small
+    @State private var ringWidth: ContactImageEntry.RingWidth = .regular
+    @State private var ringGap: ContactImageEntry.RingGap = .small
+    @State private var layout: ContactImageLayout = .default
+    @State private var primary: ContactImageValue = .glucose
+    @State private var top: ContactImageValue = .none
+    @State private var bottom: ContactImageValue = .trend
+    @State private var ring: ContactImageLargeRing = .none
+    @State private var fontSize: ContactImageEntry.FontSize = .regular
+    @State private var secondaryFontSize: ContactImageEntry.FontSize = .small
     @State private var fontWeight: Font.Weight = .medium
     @State private var fontWidth: Font.Width = .standard
 
-    private var previewEntry: ContactTrickEntry {
-        ContactTrickEntry(
+    private var previewEntry: ContactImageEntry {
+        ContactImageEntry(
             id: UUID(),
             name: "", // automatically set and populated
             layout: layout,
@@ -29,7 +29,7 @@ struct AddContactTrickSheet: View {
             primary: primary,
             top: top,
             bottom: bottom,
-            contactId: nil, // not needed for preview, gets set later in ContactTrickStateModel via ContactTrickManager
+            contactId: nil, // not needed for preview, gets set later in ContactImageStateModel via ContactImageManager
             hasHighContrast: hasHighContrast,
             ringWidth: ringWidth,
             ringGap: ringGap,
@@ -48,7 +48,7 @@ struct AddContactTrickSheet: View {
                     Spacer()
                     ZStack {
                         Circle()
-                            .fill(previewEntry.hasHighContrast ? .black : .white)
+                            .fill(.black)
                             .foregroundColor(.white)
                             .frame(width: 100, height: 100)
                         Image(uiImage: ContactPicture.getImage(contact: previewEntry, state: state.state))
@@ -57,7 +57,7 @@ struct AddContactTrickSheet: View {
                             .clipShape(Circle())
                         Circle()
                             .stroke(lineWidth: 2)
-                            .foregroundColor(.white)
+                            .foregroundColor(colorScheme == .dark ? .white : .black)
                             .frame(width: 100, height: 100)
                     }
                     Spacer()
@@ -69,30 +69,35 @@ struct AddContactTrickSheet: View {
                     // Layout Section
                     Section(header: Text("Style")) {
                         Picker("Layout", selection: $layout) {
-                            ForEach(ContactTrickLayout.allCases, id: \.id) { layout in
+                            ForEach(ContactImageLayout.allCases, id: \.id) { layout in
                                 Text(layout.displayName).tag(layout)
                             }
-                        }
+                        }.onChange(of: layout, { oldLayout, newLayout in
+                            if oldLayout != newLayout, newLayout == .split {
+                                top = .glucose
+                            } else {
+                                top = .none
+                            }
+                        })
                         Toggle("High Contrast Mode", isOn: $hasHighContrast)
                     }.listRowBackground(Color.chart)
 
                     // Primary Value Section
                     Section(header: Text("Display Values")) {
-                        if layout == .single {
+                        Picker("Top Value", selection: $top) {
+                            ForEach(ContactImageValue.allCases, id: \.id) { value in
+                                Text(value.displayName).tag(value)
+                            }
+                        }
+                        if layout == .default {
                             Picker("Primary", selection: $primary) {
-                                ForEach(ContactTrickValue.allCases, id: \.id) { value in
+                                ForEach(ContactImageValue.allCases, id: \.id) { value in
                                     Text(value.displayName).tag(value)
                                 }
                             }
                         }
-
-                        Picker("Top Value", selection: $top) {
-                            ForEach(ContactTrickValue.allCases, id: \.id) { value in
-                                Text(value.displayName).tag(value)
-                            }
-                        }
                         Picker("Bottom Value", selection: $bottom) {
-                            ForEach(ContactTrickValue.allCases, id: \.id) { value in
+                            ForEach(ContactImageValue.allCases, id: \.id) { value in
                                 Text(value.displayName).tag(value)
                             }
                         }
@@ -102,19 +107,19 @@ struct AddContactTrickSheet: View {
                     // Ring Settings Section
                     Section(header: Text("Ring Settings")) {
                         Picker("Ring Type", selection: $ring) {
-                            ForEach(ContactTrickLargeRing.allCases, id: \.self) { ring in
+                            ForEach(ContactImageLargeRing.allCases, id: \.self) { ring in
                                 Text(ring.displayName).tag(ring)
                             }
                         }
 
                         if ring != .none {
                             Picker("Ring Width", selection: $ringWidth) {
-                                ForEach(ContactTrickEntry.RingWidth.allCases, id: \.self) { width in
+                                ForEach(ContactImageEntry.RingWidth.allCases, id: \.self) { width in
                                     Text(width.displayName).tag(width)
                                 }
                             }
                             Picker("Ring Gap", selection: $ringGap) {
-                                ForEach(ContactTrickEntry.RingGap.allCases, id: \.self) { gap in
+                                ForEach(ContactImageEntry.RingGap.allCases, id: \.self) { gap in
                                     Text(gap.displayName).tag(gap)
                                 }
                             }
@@ -124,7 +129,9 @@ struct AddContactTrickSheet: View {
                     // Font Settings Section
                     Section(header: Text("Font Settings")) {
                         fontSizePicker
-                        secondaryFontSizePicker
+                        if layout == .split {
+                            secondaryFontSizePicker
+                        }
                         fontWeightPicker
                         fontWidthPicker
                     }.listRowBackground(Color.chart)
@@ -202,7 +209,7 @@ struct AddContactTrickSheet: View {
 
     private var fontSizePicker: some View {
         Picker("Font Size", selection: $fontSize) {
-            ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
+            ForEach(ContactImageEntry.FontSize.allCases, id: \.self) { size in
                 Text(size.displayName).tag(size)
             }
         }
@@ -210,7 +217,7 @@ struct AddContactTrickSheet: View {
 
     private var secondaryFontSizePicker: some View {
         Picker("Secondary Font Size", selection: $secondaryFontSize) {
-            ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
+            ForEach(ContactImageEntry.FontSize.allCases, id: \.self) { size in
                 Text(size.displayName).tag(size)
             }
         }
@@ -230,7 +237,7 @@ struct AddContactTrickSheet: View {
     private var fontWidthPicker: some View {
         Picker("Font Width", selection: $fontWidth) {
             ForEach(
-                [Font.Width.standard, Font.Width.condensed, Font.Width.expanded],
+                [Font.Width.standard, Font.Width.expanded],
                 id: \.self
             ) { width in
                 Text("\(width.displayName)".capitalized).tag(width)
@@ -241,7 +248,7 @@ struct AddContactTrickSheet: View {
     private func saveNewEntry() {
         // Save the currently previewed entry
         Task {
-            await state.createAndSaveContactTrick(entry: previewEntry, name: "Trio \(state.contactTrickEntries.count + 1)")
+            await state.createAndSaveContactImage(entry: previewEntry, name: "Trio \(state.contactImageEntries.count + 1)")
             dismiss()
         }
     }

+ 50 - 41
FreeAPS/Sources/Modules/ContactTrick/View/ContactTrickDetailView.swift

@@ -1,19 +1,19 @@
 import SwiftUI
 
-struct ContactTrickDetailView: View {
+struct ContactImageDetailView: View {
     @Environment(\.dismiss) var dismiss
     @Environment(\.colorScheme) var colorScheme
     @Environment(AppState.self) var appState
 
-    @ObservedObject var state: ContactTrick.StateModel
+    @ObservedObject var state: ContactImage.StateModel
 
-    @State private var contactTrickEntry: ContactTrickEntry
-    @State private var initialContactTrickEntry: ContactTrickEntry
+    @State private var contactImageEntry: ContactImageEntry
+    @State private var initialContactImageEntry: ContactImageEntry
 
-    init(entry: ContactTrickEntry, state: ContactTrick.StateModel) {
+    init(entry: ContactImageEntry, state: ContactImage.StateModel) {
         self.state = state
-        _contactTrickEntry = State(initialValue: entry)
-        _initialContactTrickEntry = State(initialValue: entry)
+        _contactImageEntry = State(initialValue: entry)
+        _initialContactImageEntry = State(initialValue: entry)
     }
 
     var body: some View {
@@ -23,16 +23,16 @@ struct ContactTrickDetailView: View {
                 Spacer()
                 ZStack {
                     Circle()
-                        .fill(contactTrickEntry.hasHighContrast ? .black : .white)
+                        .fill(.black)
                         .foregroundColor(.white)
                         .frame(width: 100, height: 100)
-                    Image(uiImage: ContactPicture.getImage(contact: contactTrickEntry, state: state.state))
+                    Image(uiImage: ContactPicture.getImage(contact: contactImageEntry, state: state.state))
                         .resizable()
                         .frame(width: 100, height: 100)
                         .clipShape(Circle())
                     Circle()
                         .stroke(lineWidth: 2)
-                        .foregroundColor(.white)
+                        .foregroundColor(colorScheme == .dark ? .white : .black)
                         .frame(width: 100, height: 100)
                 }
                 Spacer()
@@ -42,31 +42,38 @@ struct ContactTrickDetailView: View {
 
             Form {
                 Section(header: Text("Style")) {
-                    Picker("Layout", selection: $contactTrickEntry.layout) {
-                        ForEach(ContactTrickLayout.allCases, id: \.id) { layout in
+                    Picker("Layout", selection: $contactImageEntry.layout) {
+                        ForEach(ContactImageLayout.allCases, id: \.id) { layout in
                             Text(layout.displayName).tag(layout)
                         }
-                    }
-                    Toggle("High Contrast Mode", isOn: $contactTrickEntry.hasHighContrast)
+                    }.onChange(of: contactImageEntry.layout, { oldLayout, newLayout in
+                        if oldLayout != newLayout, newLayout == .split {
+                            contactImageEntry.top = .glucose
+                        } else {
+                            contactImageEntry.top = .none
+                        }
+                    })
+
+                    Toggle("High Contrast Mode", isOn: $contactImageEntry.hasHighContrast)
                 }.listRowBackground(Color.chart)
 
                 Section(header: Text("Display Values")) {
-                    if contactTrickEntry.layout == .single {
-                        Picker("Primary", selection: $contactTrickEntry.primary) {
-                            ForEach(ContactTrickValue.allCases, id: \.id) { value in
-                                Text(value.displayName).tag(value)
-                            }
+                    Picker("Top Value", selection: $contactImageEntry.top) {
+                        ForEach(ContactImageValue.allCases, id: \.id) { value in
+                            Text(value.displayName).tag(value)
                         }
                     }
 
-                    Picker("Top Value", selection: $contactTrickEntry.top) {
-                        ForEach(ContactTrickValue.allCases, id: \.id) { value in
-                            Text(value.displayName).tag(value)
+                    if contactImageEntry.layout == .default {
+                        Picker("Primary", selection: $contactImageEntry.primary) {
+                            ForEach(ContactImageValue.allCases, id: \.id) { value in
+                                Text(value.displayName).tag(value)
+                            }
                         }
                     }
 
-                    Picker("Bottom Value", selection: $contactTrickEntry.bottom) {
-                        ForEach(ContactTrickValue.allCases, id: \.id) { value in
+                    Picker("Bottom Value", selection: $contactImageEntry.bottom) {
+                        ForEach(ContactImageValue.allCases, id: \.id) { value in
                             Text(value.displayName).tag(value)
                         }
                     }
@@ -74,20 +81,20 @@ struct ContactTrickDetailView: View {
 
                 // Ring Settings Section
                 Section(header: Text("Ring Settings")) {
-                    Picker("Ring Type", selection: $contactTrickEntry.ring) {
-                        ForEach(ContactTrickLargeRing.allCases, id: \.self) { ring in
+                    Picker("Ring Type", selection: $contactImageEntry.ring) {
+                        ForEach(ContactImageLargeRing.allCases, id: \.self) { ring in
                             Text(ring.displayName).tag(ring)
                         }
                     }
 
-                    if contactTrickEntry.ring != .none {
-                        Picker("Ring Width", selection: $contactTrickEntry.ringWidth) {
-                            ForEach(ContactTrickEntry.RingWidth.allCases, id: \.self) { width in
+                    if contactImageEntry.ring != .none {
+                        Picker("Ring Width", selection: $contactImageEntry.ringWidth) {
+                            ForEach(ContactImageEntry.RingWidth.allCases, id: \.self) { width in
                                 Text(width.displayName).tag(width)
                             }
                         }
-                        Picker("Ring Gap", selection: $contactTrickEntry.ringGap) {
-                            ForEach(ContactTrickEntry.RingGap.allCases, id: \.self) { gap in
+                        Picker("Ring Gap", selection: $contactImageEntry.ringGap) {
+                            ForEach(ContactImageEntry.RingGap.allCases, id: \.self) { gap in
                                 Text(gap.displayName).tag(gap)
                             }
                         }
@@ -97,7 +104,9 @@ struct ContactTrickDetailView: View {
                 // Font Settings Section
                 Section(header: Text("Font Settings")) {
                     fontSizePicker
-                    secondaryFontSizePicker
+                    if contactImageEntry.layout == .split {
+                        secondaryFontSizePicker
+                    }
                     fontWeightPicker
                     fontWidthPicker
                 }.listRowBackground(Color.chart)
@@ -146,13 +155,13 @@ struct ContactTrickDetailView: View {
 
     private func saveChanges() {
         Task {
-            await state.updateContact(with: contactTrickEntry)
+            await state.updateContact(with: contactImageEntry)
             dismiss()
         }
     }
 
     var stickySaveButton: some View {
-        var isUnchanged: Bool { initialContactTrickEntry == contactTrickEntry }
+        var isUnchanged: Bool { initialContactImageEntry == contactImageEntry }
 
         return ZStack {
             Rectangle()
@@ -177,23 +186,23 @@ struct ContactTrickDetailView: View {
     }
 
     private var fontSizePicker: some View {
-        Picker("Font Size", selection: $contactTrickEntry.fontSize) {
-            ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
+        Picker("Font Size", selection: $contactImageEntry.fontSize) {
+            ForEach(ContactImageEntry.FontSize.allCases, id: \.self) { size in
                 Text(size.displayName).tag(size)
             }
         }
     }
 
     private var secondaryFontSizePicker: some View {
-        Picker("Secondary Font Size", selection: $contactTrickEntry.secondaryFontSize) {
-            ForEach(ContactTrickEntry.FontSize.allCases, id: \.self) { size in
+        Picker("Secondary Font Size", selection: $contactImageEntry.secondaryFontSize) {
+            ForEach(ContactImageEntry.FontSize.allCases, id: \.self) { size in
                 Text(size.displayName).tag(size)
             }
         }
     }
 
     private var fontWeightPicker: some View {
-        Picker("Font Weight", selection: $contactTrickEntry.fontWeight) {
+        Picker("Font Weight", selection: $contactImageEntry.fontWeight) {
             ForEach(
                 [Font.Weight.light, Font.Weight.regular, Font.Weight.medium, Font.Weight.bold, Font.Weight.black],
                 id: \.self
@@ -204,9 +213,9 @@ struct ContactTrickDetailView: View {
     }
 
     private var fontWidthPicker: some View {
-        Picker("Font Width", selection: $contactTrickEntry.fontWidth) {
+        Picker("Font Width", selection: $contactImageEntry.fontWidth) {
             ForEach(
-                [Font.Width.standard, Font.Width.condensed, Font.Width.expanded],
+                [Font.Width.standard, Font.Width.expanded],
                 id: \.self
             ) { width in
                 Text("\(width.displayName)".capitalized).tag(width)

+ 10 - 10
FreeAPS/Sources/Modules/ContactTrick/View/ContactTrickRootView.swift

@@ -3,7 +3,7 @@ import ContactsUI
 import SwiftUI
 import Swinject
 
-extension ContactTrick {
+extension ContactImage {
     struct RootView: BaseView {
         let resolver: Resolver
         @State var state = StateModel()
@@ -14,7 +14,7 @@ extension ContactTrick {
 
         var body: some View {
             Form {
-                contactTrickList
+                contactItemsList
             }
             .onAppear(perform: configureView)
             .navigationTitle("Contacts Configuration")
@@ -34,13 +34,13 @@ extension ContactTrick {
                 }
             }
             .sheet(isPresented: $isAddSheetPresented) {
-                AddContactTrickSheet(state: state)
+                AddContactImageSheet(state: state)
             }
         }
 
-        private var contactTrickList: some View {
+        private var contactItemsList: some View {
             List {
-                if state.contactTrickEntries.isEmpty {
+                if state.contactImageEntries.isEmpty {
                     Section(
                         header: Text(""),
                         content: {
@@ -48,12 +48,12 @@ extension ContactTrick {
                         }
                     ).listRowBackground(Color.chart)
                 } else {
-                    ForEach(state.contactTrickEntries, id: \.id) { entry in
-                        NavigationLink(destination: ContactTrickDetailView(entry: entry, state: state)) {
+                    ForEach(state.contactImageEntries, id: \.id) { entry in
+                        NavigationLink(destination: ContactImageDetailView(entry: entry, state: state)) {
                             HStack {
                                 ZStack {
                                     Circle()
-                                        .fill(entry.hasHighContrast ? .black : .white)
+                                        .fill(.black)
                                         .foregroundColor(.white)
                                         .frame(width: 40, height: 40)
 
@@ -64,7 +64,7 @@ extension ContactTrick {
 
                                     Circle()
                                         .stroke(lineWidth: 2)
-                                        .foregroundColor(.white)
+                                        .foregroundColor(colorScheme == .dark ? .white : .black)
                                         .frame(width: 40, height: 40)
                                 }
 
@@ -80,7 +80,7 @@ extension ContactTrick {
         private func onDelete(offsets: IndexSet) {
             Task {
                 for offset in offsets {
-                    let entry = state.contactTrickEntries[offset]
+                    let entry = state.contactImageEntries[offset]
                     await state.deleteContact(entry: entry)
                 }
             }

+ 0 - 8
FreeAPS/Sources/Modules/ContactTrick/ContactTrickDataFlow.swift

@@ -1,8 +0,0 @@
-import Combine
-import Foundation
-
-enum ContactTrick {
-    enum Config {}
-}
-
-protocol ContactTrickProvider: Provider {}

+ 0 - 6
FreeAPS/Sources/Modules/ContactTrick/ContactTrickProvider.swift

@@ -1,6 +0,0 @@
-import Combine
-import Foundation
-
-extension ContactTrick {
-    final class Provider: BaseProvider, ContactTrickProvider {}
-}

+ 2 - 2
FreeAPS/Sources/Modules/WatchConfig/View/WatchConfigAppleWatchView.swift

@@ -97,12 +97,12 @@ struct WatchConfigAppleWatchView: BaseView {
             )
 
             Section(
-                header: Text("Contact Trick"),
+                header: Text("Contact Image"),
                 content: {
                     VStack {
                         HStack {
                             NavigationLink("Contacts Configuration") {
-                                ContactTrick.RootView(resolver: resolver)
+                                ContactImage.RootView(resolver: resolver)
                             }.foregroundStyle(Color.accentColor)
                         }
                     }

+ 3 - 3
FreeAPS/Sources/Router/Screen.swift

@@ -42,7 +42,7 @@ enum Screen: Identifiable, Hashable {
     case liveActivitySettings
     case liveActivityBottomRowSettings
     case calendarEventSettings
-    case contactTrick
+    case contactImage
     case serviceSettings
     case remoteControlConfig
     case autosensSettings
@@ -139,8 +139,8 @@ extension Screen {
             LiveActivityWidgetConfiguration(resolver: resolver, state: LiveActivitySettings.StateModel())
         case .calendarEventSettings:
             CalendarEventSettings.RootView(resolver: resolver)
-        case .contactTrick:
-            ContactTrick.RootView(resolver: resolver)
+        case .contactImage:
+            ContactImage.RootView(resolver: resolver)
         case .serviceSettings:
             ServicesView(resolver: resolver, state: Settings.StateModel())
         case .autosensSettings:

+ 25 - 39
FreeAPS/Sources/Services/ContactTrick/ContactTrickManager.swift

@@ -3,31 +3,31 @@ import Contacts
 import CoreData
 import Swinject
 
-protocol ContactTrickManagerDelegate: AnyObject {
-    func contactTrickManagerDidUpdateState(_ state: ContactTrickState)
+protocol ContactImageManagerDelegate: AnyObject {
+    func contactImageManagerDidUpdateState(_ state: ContactImageState)
 }
 
-protocol ContactTrickManager {
-    var delegate: ContactTrickManagerDelegate? { get set }
+protocol ContactImageManager {
+    var delegate: ContactImageManagerDelegate? { get set }
     func requestAccess() async -> Bool
     func createContact(name: String) async -> String?
     func deleteContact(withIdentifier identifier: String) async -> Bool
     func updateContact(withIdentifier identifier: String, newName: String) async -> Bool
-    @MainActor func updateContactTrickState() async
+    @MainActor func updateContactImageState() async
     func setImageForContact(contactId: String) async
     func validateContactExists(withIdentifier identifier: String) async -> Bool
 }
 
-final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
+final class BaseContactImageManager: NSObject, ContactImageManager, Injectable {
     @Injected() private var glucoseStorage: GlucoseStorage!
-    @Injected() private var contactTrickStorage: ContactTrickStorage!
+    @Injected() private var contactImageStorage: ContactImageStorage!
     @Injected() private var settingsManager: SettingsManager!
     @Injected() private var fileStorage: FileStorage!
 
     private let contactStore = CNContactStore()
 
     // Make it read-only from outside the class
-    private(set) var state = ContactTrickState()
+    private(set) var state = ContactImageState()
 
     private let viewContext = CoreDataStack.shared.persistentContainer.viewContext
     private let backgroundContext = CoreDataStack.shared.newTaskContext()
@@ -46,7 +46,7 @@ final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
         return formatter
     }
 
-    weak var delegate: ContactTrickManagerDelegate?
+    weak var delegate: ContactImageManagerDelegate?
 
     init(resolver: Resolver) {
         super.init()
@@ -63,7 +63,7 @@ final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
             .sink { [weak self] _ in
                 guard let self = self else { return }
                 Task {
-                    await self.updateContactTrickState()
+                    await self.updateContactImageState()
                     await self.updateContactImages()
                 }
             }
@@ -75,24 +75,10 @@ final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
     // MARK: - Core Data observation
 
     private func registerHandlers() {
-        /*
-         TODO: - Do we really need to update in both cases, i.e. when OrefDetermination entity AND GlucoseStored entity have received updates ?
-         The main use case is showing glucose values and both updates happen ~ at the same time and if a new glucose value arrives the latest Determination gets fetched with that as well. Moreover, we don't need to update on Determination updates at all if the user hasn't chosen to display anything Determination related
-         */
-//
-//        coreDataPublisher?.filterByEntityName("OrefDetermination").sink { [weak self] _ in
-//            guard let self = self else { return }
-//            Task {
-//                await self.updateContactTrickState()
-//                await self.updateContactImages()
-//            }
-//        }.store(in: &subscriptions)
-
-        // Only needed for manual glucose entries
-        coreDataPublisher?.filterByEntityName("GlucoseStored").sink { [weak self] _ in
+        coreDataPublisher?.filterByEntityName("OrefDetermination").sink { [weak self] _ in
             guard let self = self else { return }
             Task {
-                await self.updateContactTrickState()
+                await self.updateContactImageState()
                 await self.updateContactImages()
             }
         }.store(in: &subscriptions)
@@ -185,13 +171,13 @@ final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
         return nil
     }
 
-    // MARK: - Configure ContactTrickState in order to update ContactTrickImage
+    // MARK: - Configure ContactImageState in order to update ContactImageImage
 
-    /// Updates the `ContactTrickState` with the latest data from Core Data.
+    /// Updates the `ContactImageState` with the latest data from Core Data.
     /// This function fetches glucose values and determination entries, processes the data,
     /// and updates the `state` object, which represents the current contact trick state.
     /// - Important: This function must be called on the main actor to ensure thread safety. Otherwise, we would need to ensure thread safety by either using an actor or a perform closure
-    @MainActor func updateContactTrickState() async {
+    @MainActor func updateContactImageState() async {
         // Get NSManagedObjectIDs on backgroundContext
         let glucoseValuesIds = await fetchGlucose()
         let determinationIds = await fetchlastDetermination()
@@ -256,7 +242,7 @@ final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
 
         // Notify delegate about state update on main thread
         await MainActor.run {
-            delegate?.contactTrickManagerDidUpdateState(state)
+            delegate?.contactImageManagerDidUpdateState(state)
         }
     }
 
@@ -272,14 +258,14 @@ final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
     }
 
     /// Sets the image for a specific contact in Apple Contacts.
-    /// This function fetches the associated `ContactTrickEntry` for the provided contact ID, generates an image
-    /// based on the current `ContactTrickState`, and updates the contact in the user's Apple Contacts.
+    /// This function fetches the associated `ContactImageEntry` for the provided contact ID, generates an image
+    /// based on the current `ContactImageState`, and updates the contact in the user's Apple Contacts.
     /// - Parameter contactId: The unique identifier of the contact in Apple Contacts.
     /// - Important: This function should be called when a new contact is created and needs its initial image set.
     func setImageForContact(contactId: String) async {
-        guard let contactEntry = await contactTrickStorage.fetchContactTrickEntries().first(where: { $0.contactId == contactId })
+        guard let contactEntry = await contactImageStorage.fetchContactImageEntries().first(where: { $0.contactId == contactId })
         else {
-            debugPrint("\(DebuggingIdentifiers.failed) No matching ContactTrickEntry found for contact ID: \(contactId)")
+            debugPrint("\(DebuggingIdentifiers.failed) No matching ContactImageEntry found for contact ID: \(contactId)")
             return
         }
 
@@ -316,12 +302,12 @@ final class BaseContactTrickManager: NSObject, ContactTrickManager, Injectable {
     }
 
     /// Updates the images of all contacts stored in Core Data.
-    /// This function iterates through all stored `ContactTrickEntry` objects, generates a new contact image
-    /// based on the current `ContactTrickState`, and updates the image in the user's Apple Contacts.
-    /// - Important: This function should be called whenever the `ContactTrickState` changes.
+    /// This function iterates through all stored `ContactImageEntry` objects, generates a new contact image
+    /// based on the current `ContactImageState`, and updates the image in the user's Apple Contacts.
+    /// - Important: This function should be called whenever the `ContactImageState` changes.
     func updateContactImages() async {
-        // Iterate through all stored ContactTrickEntry objects
-        for contactEntry in await contactTrickStorage.fetchContactTrickEntries() {
+        // Iterate through all stored ContactImageEntry objects
+        for contactEntry in await contactImageStorage.fetchContactImageEntries() {
             // Ensure the contact has a valid contact ID
             guard let contactId = contactEntry.contactId else { continue }
 

+ 1 - 1
FreeAPS/Sources/Services/ContactTrick/ContactTrickState.swift

@@ -1,6 +1,6 @@
 import Foundation
 
-struct ContactTrickState: Codable {
+struct ContactImageState: Codable {
     var glucose: String?
     var trend: String?
     var delta: String?

+ 49 - 51
FreeAPS/Sources/Services/ContactTrick/ContactTrickPicture.swift

@@ -6,8 +6,8 @@ struct ContactPicture: View {
         static let lag: TimeInterval = 30
     }
 
-    @Binding var contact: ContactTrickEntry
-    @Binding var state: ContactTrickState
+    @Binding var contact: ContactImageEntry
+    @Binding var state: ContactImageState
 
     private static let formatter: DateFormatter = {
         let formatter = DateFormatter()
@@ -16,18 +16,14 @@ struct ContactPicture: View {
     }()
 
     static func getImage(
-        contact: ContactTrickEntry,
-        state: ContactTrickState
+        contact: ContactImageEntry,
+        state: ContactImageState
     ) -> UIImage {
         let width = 1024.0
         let height = 1024.0
         var rect = CGRect(x: 0, y: 0, width: width, height: height)
-        let textColor: Color = contact.hasHighContrast ?
-            Color(red: 250 / 256, green: 250 / 256, blue: 250 / 256) :
-            Color(red: 20 / 256, green: 20 / 256, blue: 20 / 256)
-        let secondaryTextColor: Color = contact.hasHighContrast ?
-            Color(red: 220 / 256, green: 220 / 256, blue: 220 / 256) :
-            Color(red: 40 / 256, green: 40 / 256, blue: 40 / 256)
+        let textColor: Color = .white.opacity(contact.hasHighContrast ? 1 : 0.8)
+        let secondaryTextColor: Color = .loopGray.opacity(contact.hasHighContrast ? 1 : 0.8)
         let fontWeight = contact.fontWeight
 
         UIGraphicsBeginImageContext(rect.size)
@@ -66,7 +62,7 @@ struct ContactPicture: View {
         }
 
         switch contact.layout {
-        case .single:
+        case .default:
             let showTop = contact.top != .none
             let showBottom = contact.bottom != .none
 
@@ -227,9 +223,9 @@ struct ContactPicture: View {
     }
 
     private static func displayPiece(
-        value: ContactTrickValue,
-        contact: ContactTrickEntry,
-        state: ContactTrickState,
+        value: ContactImageValue,
+        contact: ContactImageEntry,
+        state: ContactImageState,
         rect: CGRect,
         fitHeigh: Bool,
         fontSize: Int,
@@ -277,6 +273,8 @@ struct ContactPicture: View {
         let textColor: Color = switch value {
         case .cob:
             .loopYellow
+        case .iob:
+            .insulin
         case .glucose:
             dynamicColor
         default:
@@ -337,9 +335,9 @@ struct ContactPicture: View {
     }
 
     private static func drawRing(
-        ring: ContactTrickLargeRing,
-        contact: ContactTrickEntry,
-        state: ContactTrickState,
+        ring: ContactImageLargeRing,
+        contact: ContactImageEntry,
+        state: ContactImageState,
         rect: CGRect,
         strokeWidth: Double
     ) {
@@ -566,8 +564,8 @@ struct ContactPicture: View {
     }
 
     private static func ringColor(
-        contact _: ContactTrickEntry,
-        state: ContactTrickState
+        contact _: ContactImageEntry,
+        state: ContactImageState
     ) -> Color {
         guard let lastLoopDate = state.lastLoopDate else {
             return .loopGray
@@ -616,8 +614,8 @@ enum GradientDirection: Int {
 }
 
 struct ContactPicturePreview: View {
-    @Binding var contact: ContactTrickEntry
-    @Binding var state: ContactTrickState
+    @Binding var contact: ContactImageEntry
+    @Binding var state: ContactImageState
 
     var body: some View {
         ZStack {
@@ -628,7 +626,7 @@ struct ContactPicturePreview: View {
         }
         .frame(width: 256, height: 256)
         .clipShape(Circle())
-        .preferredColorScheme($contact.wrappedValue.hasHighContrast ? .dark : .light)
+        .preferredColorScheme(.dark)
     }
 }
 
@@ -636,14 +634,14 @@ struct ContactPicture_Previews: PreviewProvider {
     struct Preview: View {
         @State var rangeIndicator: Bool = true
         @State var hasHighContrast: Bool = true
-        @State var fontSize: ContactTrickEntry.FontSize = .small
+        @State var fontSize: ContactImageEntry.FontSize = .small
         @State var fontWeight: UIFont.Weight = .bold
         @State var fontName: String? = "AmericanTypewriter"
 
         var body: some View {
             ContactPicturePreview(
                 contact: .constant(
-                    ContactTrickEntry(
+                    ContactImageEntry(
                         primary: .glucose,
                         top: .delta,
                         bottom: .trend,
@@ -651,7 +649,7 @@ struct ContactPicture_Previews: PreviewProvider {
                         fontWeight: .medium
                     )
                 ),
-                state: .constant(ContactTrickState(
+                state: .constant(ContactImageState(
                     glucose: "6.8",
                     trend: "↗︎",
                     delta: "+0.2",
@@ -662,7 +660,7 @@ struct ContactPicture_Previews: PreviewProvider {
 
 //            ContactPicturePreview(
 //                contact: .constant(
-//                    ContactTrickEntry(
+//                    ContactImageEntry(
 //                        ring: .iob,
 //                        primary: .glucose,
 //                        bottom: .trend,
@@ -670,7 +668,7 @@ struct ContactPicture_Previews: PreviewProvider {
 //                        fontWeight: .medium
 //                    )
 //                ),
-//                state: .constant(ContactTrickState(
+//                state: .constant(ContactImageState(
 //                    glucose: "6.8",
 //                    trend: "↗︎",
 //                    iob: 6.1,
@@ -681,7 +679,7 @@ struct ContactPicture_Previews: PreviewProvider {
 
             ContactPicturePreview(
                 contact: .constant(
-                    ContactTrickEntry(
+                    ContactImageEntry(
                         primary: .glucose,
                         top: .ring,
                         bottom: .trend,
@@ -689,7 +687,7 @@ struct ContactPicture_Previews: PreviewProvider {
                         fontWeight: .medium
                     )
                 ),
-                state: .constant(ContactTrickState(
+                state: .constant(ContactImageState(
                     glucose: "6.8",
                     trend: "↗︎",
                     lastLoopDate: .now
@@ -699,7 +697,7 @@ struct ContactPicture_Previews: PreviewProvider {
 
             ContactPicturePreview(
                 contact: .constant(
-                    ContactTrickEntry(
+                    ContactImageEntry(
                         ring: .loop,
                         primary: .glucose,
                         top: .none,
@@ -708,7 +706,7 @@ struct ContactPicture_Previews: PreviewProvider {
                         fontWeight: .medium
                     )
                 ),
-                state: .constant(ContactTrickState(
+                state: .constant(ContactImageState(
                     glucose: "8.8",
                     trend: "→",
                     lastLoopDate: .now
@@ -717,7 +715,7 @@ struct ContactPicture_Previews: PreviewProvider {
 
             ContactPicturePreview(
                 contact: .constant(
-                    ContactTrickEntry(
+                    ContactImageEntry(
                         ring: .loop,
                         primary: .glucose,
                         top: .none,
@@ -726,7 +724,7 @@ struct ContactPicture_Previews: PreviewProvider {
                         fontWeight: .medium
                     )
                 ),
-                state: .constant(ContactTrickState(
+                state: .constant(ContactImageState(
                     glucose: "6.8",
                     lastLoopDate: .now - 7.minutes,
                     eventualBG: "6.2"
@@ -735,7 +733,7 @@ struct ContactPicture_Previews: PreviewProvider {
 
             ContactPicturePreview(
                 contact: .constant(
-                    ContactTrickEntry(
+                    ContactImageEntry(
                         ring: .loop,
                         primary: .lastLoopDate,
                         top: .none,
@@ -744,7 +742,7 @@ struct ContactPicture_Previews: PreviewProvider {
                         fontWeight: .medium
                     )
                 ),
-                state: .constant(ContactTrickState(
+                state: .constant(ContactImageState(
                     glucose: "6.8",
                     trend: "↗︎",
                     lastLoopDate: .now - 2.minutes
@@ -753,7 +751,7 @@ struct ContactPicture_Previews: PreviewProvider {
 
             ContactPicturePreview(
                 contact: .constant(
-                    ContactTrickEntry(
+                    ContactImageEntry(
                         ring: .loop,
                         primary: .glucose,
                         top: .none,
@@ -762,7 +760,7 @@ struct ContactPicture_Previews: PreviewProvider {
                         fontWeight: .medium
                     )
                 ),
-                state: .constant(ContactTrickState(
+                state: .constant(ContactImageState(
                     glucose: "6.8",
                     lastLoopDate: .now,
                     iob: 6.1,
@@ -773,7 +771,7 @@ struct ContactPicture_Previews: PreviewProvider {
 
             ContactPicturePreview(
                 contact: .constant(
-                    ContactTrickEntry(
+                    ContactImageEntry(
                         layout: .split,
                         top: .iob,
                         bottom: .cob,
@@ -781,7 +779,7 @@ struct ContactPicture_Previews: PreviewProvider {
                         fontWeight: .medium
                     )
                 ),
-                state: .constant(ContactTrickState(
+                state: .constant(ContactImageState(
                     iob: 1.5,
                     iobText: "1.5",
                     cob: 25,
@@ -791,8 +789,8 @@ struct ContactPicture_Previews: PreviewProvider {
 
 //            ContactPicturePreview(
 //                contact: .constant(
-//                    ContactTrickEntry(
-//                        layout: .single,
+//                    ContactImageEntry(
+//                        layout: .default,
 //                        ring: .iobcob,
 //                        primary: .none,
 //                        ringWidth: .regular,
@@ -801,7 +799,7 @@ struct ContactPicture_Previews: PreviewProvider {
 //                        fontWeight: .medium
 //                    )
 //                ),
-//                state: .constant(ContactTrickState(
+//                state: .constant(ContactImageState(
 //                    iob: 1,
 //                    iobText: "5.5",
 //                    cob: 25,
@@ -813,15 +811,15 @@ struct ContactPicture_Previews: PreviewProvider {
 //
 //            ContactPicturePreview(
 //                contact: .constant(
-//                    ContactTrickEntry(
-//                        layout: .single,
+//                    ContactImageEntry(
+//                        layout: .default,
 //                        ring: .iobcob,
 //                        primary: .none,
 //                        fontSize: fontSize,
 //                        fontWeight: .medium
 //                    )
 //                ),
-//                state: .constant(ContactTrickState(
+//                state: .constant(ContactImageState(
 //                    iob: -0.2,
 //                    iobText: "0.0",
 //                    cob: 0,
@@ -833,15 +831,15 @@ struct ContactPicture_Previews: PreviewProvider {
 //
 //            ContactPicturePreview(
 //                contact: .constant(
-//                    ContactTrickEntry(
-//                        layout: .single,
+//                    ContactImageEntry(
+//                        layout: .default,
 //                        ring: .iobcob,
 //                        primary: .none,
 //                        fontSize: fontSize,
 //                        fontWeight: .medium
 //                    )
 //                ),
-//                state: .constant(ContactTrickState(
+//                state: .constant(ContactImageState(
 //                    iob: 10,
 //                    iobText: "0.0",
 //                    cob: 120,
@@ -853,8 +851,8 @@ struct ContactPicture_Previews: PreviewProvider {
 //
 //            ContactPicturePreview(
 //                contact: .constant(
-//                    ContactTrickEntry(
-//                        layout: .single,
+//                    ContactImageEntry(
+//                        layout: .default,
 //                        ring: .iobcob,
 //                        primary: .glucose,
 //                        bottom: .trend,
@@ -862,7 +860,7 @@ struct ContactPicture_Previews: PreviewProvider {
 //                        fontWeight: .medium
 //                    )
 //                ),
-//                state: .constant(ContactTrickState(
+//                state: .constant(ContactImageState(
 //                    glucose: "6.8",
 //                    trend: "↗︎",
 //                    iob: 5.5,

+ 1 - 1
Model/Classes+Properties/ContactTrickEntryStored+CoreDataClass.swift

@@ -1,4 +1,4 @@
 import CoreData
 import Foundation
 
-@objc(ContactTrickEntryStored) public class ContactTrickEntryStored: NSManagedObject {}
+@objc(ContactImageEntryStored) public class ContactImageEntryStored: NSManagedObject {}

+ 3 - 3
Model/Classes+Properties/ContactTrickEntryStored+CoreDataProperties.swift

@@ -1,9 +1,9 @@
 import CoreData
 import Foundation
 
-public extension ContactTrickEntryStored {
-    @nonobjc class func fetchRequest() -> NSFetchRequest<ContactTrickEntryStored> {
-        NSFetchRequest<ContactTrickEntryStored>(entityName: "ContactTrickEntryStored")
+public extension ContactImageEntryStored {
+    @nonobjc class func fetchRequest() -> NSFetchRequest<ContactImageEntryStored> {
+        NSFetchRequest<ContactImageEntryStored>(entityName: "ContactImageEntryStored")
     }
 
     @NSManaged var name: String

+ 1 - 1
Model/TrioCoreDataPersistentContainer.xcdatamodeld/TrioCoreDataPersistentContainer.xcdatamodel/contents

@@ -25,7 +25,7 @@
             <fetchIndexElement property="isFPU" type="Binary" order="ascending"/>
         </fetchIndex>
     </entity>
-    <entity name="ContactTrickEntryStored" representedClassName="ContactTrickEntryStored" syncable="YES" codeGenerationType="class">
+    <entity name="ContactImageEntryStored" representedClassName="ContactImageEntryStored" syncable="YES" codeGenerationType="class">
         <attribute name="bottom" optional="YES" attributeType="String"/>
         <attribute name="contactId" optional="YES" attributeType="String"/>
         <attribute name="fontSize" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>