Showing 4 changed files with 14 additions and 461 deletions
+1 -1
HealthProbe/Doc/04-project/IMPLEMENTATION_STATUS.md
@@ -28,7 +28,7 @@ There are no real deployments, only test installations. Existing prototype datab
28 28
 | SQLite archive | Archive v2 schema, snapshot-level observation grouping, differential write path, v2 verification/delete bookkeeping, daily aggregate rebuilds, integrity report, v2 record reads, SQL diff/count/aggregate/provenance/consolidation-evidence APIs, large synthetic diff pagination coverage, formal timing/memory metrics, and XCTest coverage are in place; the legacy `archive_samples` mirror has been removed | Move Snapshots/Data Types from SwiftData previews to archive/cache DTOs |
29 29
 | Core Data cache | Initial programmatic Core Data model, full-cache rebuild service, read DTOs for observation/type/diff/health rows, and Dashboard archive-cache status wiring are in place | Move remaining export/report paths to cache DTOs and add targeted partial invalidation |
30 30
 | SwiftData cache | Exists; test builds now reset legacy prototype UI/archive/cache stores once for archive v2 so old SwiftData-only snapshots are not treated as backed-up observations. Metric timeout calibration, local device profile settings, operation logging, ContentView preview, Settings data maintenance, and legacy anomaly/count-drop review have moved outside SwiftData or been removed. Remaining SwiftData imports are inventoried in [`SwiftData-Retirement-Inventory.md`](SwiftData-Retirement-Inventory.md) | Treat as disposable prototype data; replace capture review actions and navigation handles before removing `ModelContainer` |
31
-| UI | Prototype exists; Snapshots/Data Types now default to the local device timeline instead of a multi-device picker. Dashboard status reads archive/cache observation rows and shows cache health, with SwiftData retained only for capture/review actions; Snapshots timeline reads archive/cache rows when available and no longer queries `SnapshotDelta` for list summaries; snapshot detail summaries/type rows and Data Types list prefer Core Data cache rows when archive observation ids exist, with Data Types diff rows no longer falling back to SwiftData `TypeCount` traversal; data type detail reads Core Data type/diff summaries and uses SQLite `diffRecords` for paged drill-down; record-change evolution and temporal distribution screens now receive DTO rows/cache input instead of querying SwiftData directly; export preview reads the archive export API before showing/exporting JSON; simplified detail mode replaces heavy charts with summary rows on small/accessibility layouts or when enabled in Settings; visible change labels now use neutral new/missing/change-review language, with SwiftData detail cache as transition fallback | Remove remaining SwiftData navigation handles |
31
+| UI | Prototype exists; Snapshots/Data Types now default to the local device timeline instead of a multi-device picker. Dashboard status reads archive/cache observation rows and shows cache health, with SwiftData retained only for capture/review actions; Snapshots timeline reads archive/cache rows when available and no longer queries `SnapshotDelta` for list summaries; snapshot detail summaries/type rows require Core Data cache rows and no longer fall back to `SnapshotDelta`/`TypeDelta`; Data Types list rows no longer fall back to SwiftData `TypeCount` traversal; data type detail reads Core Data type/diff summaries and uses SQLite `diffRecords` for paged drill-down; record-change evolution and temporal distribution screens now receive DTO rows/cache input instead of querying SwiftData directly; export preview reads the archive export API before showing/exporting JSON; simplified detail mode replaces heavy charts with summary rows on small/accessibility layouts or when enabled in Settings; visible change labels now use neutral new/missing/change-review language, with SwiftData detail cache as transition fallback | Remove remaining SwiftData navigation handles |
32 32
 | Diff/change explanation | SQL diff summaries, paged diff records, aggregate comparisons, and consolidation-evidence labels exist; legacy anomaly/count-drop review has been removed from active flows | Continue moving remaining SwiftData fallback detail paths to archive/cache DTOs |
33 33
 | Export | SQLite export preview, paged JSON writing, SHA256 manifest hashing, and `export_manifests` rows are in place for selected records and observation diffs | Fill remaining recovery-compatible envelope metadata, CSV export, relationship preservation, and reproducibility checks |
34 34
 | Legacy device support | Simplified detail UI mode is implemented for small/accessibility layouts and as a Settings toggle | Remove SwiftData dependency and validate lower deployment targets |
+3 -1
HealthProbe/Doc/04-project/Refactoring-Plan.md
@@ -226,7 +226,9 @@ Checklist:
226 226
 - [x] Observation timeline rows read Core Data cache when available and no
227 227
   longer query `SnapshotDelta` list summaries, while retaining SwiftData handles
228 228
   for detail navigation during transition.
229
-- [x] Observation detail uses cached summary/type rows plus SQLite diff summaries when archive observation ids exist.
229
+- [x] Observation detail uses cached summary/type rows plus SQLite diff
230
+  summaries and no longer falls back to legacy `SnapshotDelta`/`TypeDelta`
231
+  rows.
230 232
 - [x] Data Types list rows use Core Data cached counts plus SQLite `diffSummary` and no longer fall back to SwiftData `TypeCount` traversal.
231 233
 - [x] Data type detail uses SQLite `diffSummary` when archive observation ids exist.
232 234
 - [x] Data type new/missing drill-down pages through SQLite `diffRecords` when archive observation ids exist.
+5 -0
HealthProbe/Doc/04-project/SwiftData-Retirement-Inventory.md
@@ -123,6 +123,11 @@ The following SwiftData dependencies were removed from active flows:
123 123
   small observation contexts and builds rows from Core Data cache + SQLite
124 124
   archive diff APIs. It no longer falls back to `SnapshotDiffService.diff(...)`
125 125
   over SwiftData `TypeCount` relationships.
126
+- `HealthProbe/Views/Snapshots/SnapshotDetailView.swift` no longer queries
127
+  `SnapshotDelta`/`TypeDelta` or carries the old SwiftData type-delta/chart
128
+  fallback. Snapshot detail type rows now require archive/cache summaries; the
129
+  temporary SwiftData dependency is limited to snapshot navigation, metadata,
130
+  and PDF export handles.
126 131
 - `HealthProbe/Models/AnomalyRecord.swift`,
127 132
   `HealthProbe/Models/AnomalyType.swift`, and
128 133
   `HealthProbe/Services/AnomalyDetector.swift` were deleted. The app no longer
+5 -459
HealthProbe/Views/Snapshots/SnapshotDetailView.swift
@@ -1,4 +1,3 @@
1
-import Charts
2 1
 import SwiftUI
3 2
 import SwiftData
4 3
 import UIKit
@@ -9,7 +8,6 @@ struct SnapshotDetailView: View {
9 8
     let profile: LocalDeviceProfile?
10 9
 
11 10
     @Query(sort: \HealthSnapshot.timestamp) private var allSnapshots: [HealthSnapshot]
12
-    @Query private var allDeltas: [SnapshotDelta]
13 11
     @State private var displayedSnapshot: HealthSnapshot?
14 12
     @State private var archiveTypeRows: [SnapshotArchiveTypeRow]?
15 13
     @State private var archiveTypeError: String?
@@ -18,19 +16,6 @@ struct SnapshotDetailView: View {
18 16
         displayedSnapshot ?? snapshot
19 17
     }
20 18
 
21
-    private var currentDelta: SnapshotDelta? {
22
-        allDeltas.first { $0.toSnapshotID == currentSnapshot.id }
23
-    }
24
-
25
-    private var currentDeltaSummary: SnapshotDeltaListSummary? {
26
-        currentDelta?.listSummary
27
-    }
28
-
29
-    private var allTypeDeltas: [TypeDelta] {
30
-        (currentDelta?.typeDeltas ?? [])
31
-            .sorted { $0.displayName.localizedCompare($1.displayName) == .orderedAscending }
32
-    }
33
-
34 19
     private var archiveReloadID: String {
35 20
         [
36 21
             currentSnapshot.id.uuidString,
@@ -342,9 +327,9 @@ struct SnapshotDetailView: View {
342 327
 
343 328
     @ViewBuilder
344 329
     private func comparisonSection(baseline: HealthSnapshot) -> some View {
345
-        let delta = archiveRecordChangeCount ?? currentDeltaSummary?.absoluteRecordChangeCount ?? 0
330
+        let delta = archiveRecordChangeCount ?? 0
346 331
         let deltaPercent = computeDeltaPercent(delta: delta, baseline: baseline)
347
-        let affectedMetricCount = archiveAffectedMetricCount ?? currentDeltaSummary?.affectedMetricCount ?? 0
332
+        let affectedMetricCount = archiveAffectedMetricCount ?? 0
348 333
         let isSignificant = delta > 0 || affectedMetricCount > 0 || (deltaPercent > 10)
349 334
 
350 335
         DisclosureGroup {
@@ -359,7 +344,7 @@ struct SnapshotDetailView: View {
359 344
                     Text(days == 0 ? "Same day" : "\(days) days")
360 345
                         .foregroundStyle(.secondary)
361 346
                 }
362
-                if archiveTypeRows != nil || currentDeltaSummary != nil {
347
+                if archiveTypeRows != nil {
363 348
                     Divider()
364 349
                     DetailRow(label: "Changed Metrics") {
365 350
                         Text("\(affectedMetricCount)")
@@ -461,24 +446,9 @@ struct SnapshotDetailView: View {
461 446
             } else if baseline == nil {
462 447
                 Text("This snapshot starts the chain, so no baseline comparison is available.")
463 448
                     .foregroundStyle(.secondary)
464
-            } else if currentDelta == nil {
449
+            } else {
465 450
                 Text("Cached metric summary unavailable for this snapshot.")
466 451
                     .foregroundStyle(.secondary)
467
-            } else if allTypeDeltas.isEmpty {
468
-                Text("No data types are available for this snapshot.")
469
-                    .foregroundStyle(.secondary)
470
-            } else {
471
-                ForEach(allTypeDeltas) { typeDelta in
472
-                    NavigationLink {
473
-                        DataTypeSnapshotDetailView(
474
-                            snapshot: currentSnapshot,
475
-                            typeIdentifier: typeDelta.typeIdentifier,
476
-                            displayName: typeDelta.displayName
477
-                        )
478
-                    } label: {
479
-                        SnapshotTypeDeltaRow(typeDelta: typeDelta)
480
-                    }
481
-                }
482 452
             }
483 453
         }
484 454
     }
@@ -571,430 +541,6 @@ private struct SnapshotArchiveTypeRowView: View {
571 541
     }
572 542
 }
573 543
 
574
-private struct SnapshotTypeDeltaRow: View {
575
-    let typeDelta: TypeDelta
576
-
577
-    private var deltaLabel: String {
578
-        switch typeDelta.transition {
579
-        case .changed:
580
-            if typeDelta.countDelta == 0 {
581
-                return "Content changed"
582
-            }
583
-            let prefix = typeDelta.countDelta > 0 ? "+" : ""
584
-            return "\(prefix)\(typeDelta.countDelta) records"
585
-        case .appeared:
586
-            return "New"
587
-        case .disappeared:
588
-            return "Missing"
589
-        case .unchanged:
590
-            return "No changes"
591
-        }
592
-    }
593
-
594
-    private var deltaColor: Color {
595
-        switch typeDelta.transition {
596
-        case .disappeared:
597
-            return .criticalRed
598
-        case .changed, .appeared:
599
-            return .warningAmber
600
-        case .unchanged:
601
-            return .secondary
602
-        }
603
-    }
604
-
605
-    var body: some View {
606
-        HStack(spacing: 12) {
607
-            VStack(alignment: .leading, spacing: 3) {
608
-                Text(typeDelta.displayName)
609
-                    .font(.subheadline)
610
-                Text(typeDelta.typeIdentifier)
611
-                    .font(.caption2)
612
-                    .foregroundStyle(.secondary)
613
-                    .lineLimit(1)
614
-                    .truncationMode(.middle)
615
-            }
616
-
617
-            Spacer()
618
-
619
-            Text(deltaLabel)
620
-                .font(.caption.weight(.semibold))
621
-                .foregroundStyle(deltaColor)
622
-        }
623
-        .accessibilityElement(children: .combine)
624
-    }
625
-}
626
-
627
-private enum EvolutionXAxisMode: String, CaseIterable, Identifiable {
628
-    case time
629
-    case snapshots
630
-
631
-    var id: String { rawValue }
632
-
633
-    var title: String {
634
-        switch self {
635
-        case .time:
636
-            return "Time"
637
-        case .snapshots:
638
-            return "Snapshots"
639
-        }
640
-    }
641
-}
642
-
643
-private struct TypeEvolutionSeries: Identifiable {
644
-    let typeIdentifier: String
645
-    let displayName: String
646
-    let points: [TypeEvolutionPoint]
647
-
648
-    var id: String { typeIdentifier }
649
-
650
-    var latestPoint: TypeEvolutionPoint? {
651
-        points.max { $0.timestamp < $1.timestamp }
652
-    }
653
-
654
-    var selectedOrLatestPoint: TypeEvolutionPoint? {
655
-        points.last
656
-    }
657
-
658
-    var yDomain: ClosedRange<Double> {
659
-        let counts = points.map(\.count)
660
-        guard let minCount = counts.min(), let maxCount = counts.max() else {
661
-            return 0...1
662
-        }
663
-
664
-        if minCount == maxCount {
665
-            let lower = max(0, minCount - 1)
666
-            return Double(lower)...Double(maxCount + 1)
667
-        }
668
-
669
-        return Double(max(0, minCount))...Double(maxCount)
670
-    }
671
-}
672
-
673
-private struct TypeEvolutionPoint: Identifiable {
674
-    let snapshotID: UUID
675
-    let timestamp: Date
676
-    let count: Int
677
-
678
-    var id: UUID { snapshotID }
679
-}
680
-
681
-private struct TypeEvolutionChart: View {
682
-    let series: TypeEvolutionSeries
683
-    let contextSnapshots: [HealthSnapshot]
684
-    let xAxisMode: EvolutionXAxisMode
685
-    let selectedSnapshotID: UUID
686
-    let selectedTimestamp: Date
687
-    let snapshotNumbers: [UUID: Int]
688
-    let baselineTypeCount: TypeCount?
689
-
690
-    private struct SnapshotAxisPoint: Identifiable {
691
-        let snapshotID: UUID
692
-        let contextIndex: Int
693
-        let timestamp: Date
694
-        let count: Int
695
-
696
-        var id: UUID { snapshotID }
697
-    }
698
-
699
-    private var selectedPoint: TypeEvolutionPoint? {
700
-        series.points.first { $0.snapshotID == selectedSnapshotID }
701
-    }
702
-
703
-    private var isMissingInSelectedSnapshot: Bool {
704
-        selectedPoint == nil
705
-    }
706
-
707
-    private var previousPoint: TypeEvolutionPoint? {
708
-        guard let selectedIndex = series.points.firstIndex(where: { $0.snapshotID == selectedSnapshotID }),
709
-              selectedIndex > 0 else { return nil }
710
-        return series.points[selectedIndex - 1]
711
-    }
712
-
713
-    private var delta: Int? {
714
-        guard let selected = selectedPoint,
715
-              let previous = previousPoint,
716
-              selected.count >= 0,
717
-              previous.count >= 0 else { return nil }
718
-        return selected.count - previous.count
719
-    }
720
-
721
-    private var isSignificantChange: Bool {
722
-        guard let d = delta, let prev = previousPoint?.count, prev > 0 else { return false }
723
-        let percentChange = abs(Double(d)) / Double(prev) * 100
724
-        return percentChange > 10 || d > 0
725
-    }
726
-
727
-    private var contextPointCountLabel: String {
728
-        "\(series.points.count)/\(contextSnapshots.count) snapshots with data"
729
-    }
730
-
731
-    private var contextAxisPoints: [SnapshotAxisPoint] {
732
-        contextSnapshots.enumerated().compactMap { index, snapshot in
733
-            guard let candidateTypeCount = snapshot.typeCounts?.first(where: {
734
-                $0.typeIdentifier == series.typeIdentifier
735
-            }), candidateTypeCount.count >= 0 else {
736
-                return nil
737
-            }
738
-
739
-            return SnapshotAxisPoint(
740
-                snapshotID: snapshot.id,
741
-                contextIndex: index,
742
-                timestamp: snapshot.timestamp,
743
-                count: candidateTypeCount.count
744
-            )
745
-        }
746
-    }
747
-
748
-    private var contextAxisGroups: [[SnapshotAxisPoint]] {
749
-        guard !contextAxisPoints.isEmpty else { return [] }
750
-
751
-        var groups: [[SnapshotAxisPoint]] = []
752
-        var currentGroup: [SnapshotAxisPoint] = [contextAxisPoints[0]]
753
-
754
-        for point in contextAxisPoints.dropFirst() {
755
-            if let previous = currentGroup.last, point.contextIndex == previous.contextIndex + 1 {
756
-                currentGroup.append(point)
757
-            } else {
758
-                groups.append(currentGroup)
759
-                currentGroup = [point]
760
-            }
761
-        }
762
-
763
-        groups.append(currentGroup)
764
-        return groups
765
-    }
766
-
767
-    private var selectedContextIndex: Int? {
768
-        contextSnapshots.firstIndex { $0.id == selectedSnapshotID }
769
-    }
770
-
771
-    private var snapshotAxisValues: [Int] {
772
-        Array(contextSnapshots.indices)
773
-    }
774
-
775
-    private func snapshotAxisLabel(for index: Int) -> String {
776
-        guard contextSnapshots.indices.contains(index) else { return "\(index + 1)" }
777
-        let snapshotID = contextSnapshots[index].id
778
-        return "\(snapshotNumbers[snapshotID] ?? index + 1)"
779
-    }
780
-
781
-    private var snapshotAxisDomain: ClosedRange<Int> {
782
-        guard let first = snapshotAxisValues.first, let last = snapshotAxisValues.last else {
783
-            return 0...0
784
-        }
785
-        return first...last
786
-    }
787
-
788
-    @ViewBuilder
789
-    private var chartContent: some View {
790
-        switch xAxisMode {
791
-        case .time:
792
-            timeChart
793
-        case .snapshots:
794
-            snapshotChart
795
-        }
796
-    }
797
-
798
-    private var timeChart: some View {
799
-        Chart {
800
-            ForEach(contextSnapshots, id: \.id) { item in
801
-                RuleMark(x: .value("Timeline", item.timestamp))
802
-                    .foregroundStyle(Color.secondary.opacity(0.10))
803
-            }
804
-
805
-            RuleMark(x: .value("Selected Snapshot", selectedTimestamp))
806
-                .lineStyle(StrokeStyle(lineWidth: 1, dash: [4, 3]))
807
-                .foregroundStyle(Color.secondary.opacity(0.55))
808
-
809
-            ForEach(series.points) { point in
810
-                LineMark(
811
-                    x: .value("Date", point.timestamp),
812
-                    y: .value("Records", point.count)
813
-                )
814
-                .interpolationMethod(.linear)
815
-
816
-                PointMark(
817
-                    x: .value("Date", point.timestamp),
818
-                    y: .value("Records", point.count)
819
-                )
820
-                .symbolSize(24)
821
-
822
-                if point.snapshotID == selectedSnapshotID {
823
-                    PointMark(
824
-                        x: .value("Selected Date", point.timestamp),
825
-                        y: .value("Selected Records", point.count)
826
-                    )
827
-                    .symbolSize(64)
828
-                }
829
-            }
830
-        }
831
-    }
832
-
833
-    private var snapshotChart: some View {
834
-        Chart {
835
-            ForEach(contextSnapshots.indices, id: \.self) { index in
836
-                RuleMark(x: .value("Snapshot", index))
837
-                    .foregroundStyle(Color.secondary.opacity(0.10))
838
-            }
839
-
840
-            if let selectedContextIndex {
841
-                RuleMark(x: .value("Selected Snapshot", selectedContextIndex))
842
-                    .lineStyle(StrokeStyle(lineWidth: 1, dash: [4, 3]))
843
-                    .foregroundStyle(Color.secondary.opacity(0.55))
844
-            }
845
-
846
-            ForEach(contextAxisGroups.indices, id: \.self) { groupIndex in
847
-                let group = contextAxisGroups[groupIndex]
848
-
849
-                ForEach(group) { point in
850
-                    LineMark(
851
-                        x: .value("Snapshot", point.contextIndex),
852
-                        y: .value("Records", point.count)
853
-                    )
854
-                    .interpolationMethod(.linear)
855
-
856
-                    PointMark(
857
-                        x: .value("Snapshot", point.contextIndex),
858
-                        y: .value("Records", point.count)
859
-                    )
860
-                    .symbolSize(24)
861
-
862
-                    if point.snapshotID == selectedSnapshotID {
863
-                        PointMark(
864
-                            x: .value("Selected Snapshot", point.contextIndex),
865
-                            y: .value("Selected Records", point.count)
866
-                        )
867
-                        .symbolSize(64)
868
-                    }
869
-                }
870
-            }
871
-        }
872
-        .chartXAxis {
873
-            AxisMarks(values: snapshotAxisValues) { value in
874
-                AxisGridLine()
875
-                AxisTick()
876
-                if let rawIndex = value.as(Int.self) {
877
-                    AxisValueLabel(snapshotAxisLabel(for: rawIndex))
878
-                }
879
-            }
880
-        }
881
-        .chartXScale(domain: snapshotAxisDomain)
882
-    }
883
-
884
-    var body: some View {
885
-        VStack(alignment: .leading, spacing: 8) {
886
-            HStack(alignment: .firstTextBaseline) {
887
-                Text(series.displayName)
888
-                    .font(.subheadline.weight(.semibold))
889
-                Spacer()
890
-                VStack(alignment: .trailing, spacing: 4) {
891
-                    if let selectedPoint {
892
-                        Text("\(selectedPoint.count)")
893
-                            .font(.subheadline.monospacedDigit())
894
-                            .foregroundStyle(.secondary)
895
-                    }
896
-                    if isSignificantChange, let delta {
897
-                        SeverityBadge(delta: delta)
898
-                    }
899
-                }
900
-            }
901
-
902
-            chartContent
903
-            .chartYScale(domain: series.yDomain)
904
-            .chartXAxis {
905
-                switch xAxisMode {
906
-                case .time:
907
-                    AxisMarks(values: .automatic(desiredCount: 3))
908
-                case .snapshots:
909
-                    AxisMarks(values: snapshotAxisValues) { value in
910
-                        AxisGridLine()
911
-                        AxisTick()
912
-                        if let rawIndex = value.as(Int.self) {
913
-                            AxisValueLabel(snapshotAxisLabel(for: rawIndex))
914
-                        }
915
-                    }
916
-                }
917
-            }
918
-            .chartYAxis {
919
-                AxisMarks(position: .leading, values: .automatic(desiredCount: 3))
920
-            }
921
-            .frame(height: 120)
922
-            .foregroundStyle(Color.accentColor)
923
-
924
-            if isMissingInSelectedSnapshot {
925
-                Text("Datatype missing in this snapshot")
926
-                    .font(.caption2)
927
-                    .foregroundStyle(Color.warningAmber)
928
-            } else if series.points.count == 1 {
929
-                Text("Only one measurement")
930
-                    .font(.caption2)
931
-                    .foregroundStyle(.secondary)
932
-            }
933
-
934
-            Text(contextPointCountLabel)
935
-                .font(.caption2)
936
-                .foregroundStyle(.secondary)
937
-        }
938
-        .padding(.vertical, 4)
939
-        .accessibilityElement(children: .combine)
940
-    }
941
-}
942
-
943
-private struct SnapshotTypeCountRow: View {
944
-    let typeCount: TypeCount
945
-    let baselineTypeCount: TypeCount?
946
-
947
-    private var countText: String {
948
-        if typeCount.isUnsupported { return "Unsupported" }
949
-        if typeCount.count == -1   { return "Unavailable" }
950
-        return "\(typeCount.count)"
951
-    }
952
-
953
-    private var countColor: Color {
954
-        if typeCount.isUnsupported { return .secondary }
955
-        if typeCount.count == -1   { return Color.criticalRed }
956
-        if typeCount.quality != SnapshotQuality.complete { return Color.warningAmber }
957
-        return Color.primary
958
-    }
959
-
960
-    private var delta: Int? {
961
-        guard let b = baselineTypeCount,
962
-              typeCount.count >= 0,
963
-              b.count >= 0 else { return nil }
964
-        return typeCount.count - b.count
965
-    }
966
-
967
-    private var isSignificantChange: Bool {
968
-        guard let d = delta, let b = baselineTypeCount?.count, b > 0 else { return false }
969
-        let percentChange = abs(Double(d)) / Double(b) * 100
970
-        return percentChange > 10 || d > 0
971
-    }
972
-
973
-    var body: some View {
974
-        HStack(spacing: 12) {
975
-            VStack(alignment: .leading, spacing: 3) {
976
-                Text(typeCount.displayName)
977
-                    .font(.subheadline)
978
-                Text(typeCount.typeIdentifier)
979
-                    .font(.caption2)
980
-                    .foregroundStyle(.secondary)
981
-                    .lineLimit(1)
982
-                    .truncationMode(.middle)
983
-            }
984
-            Spacer()
985
-            VStack(alignment: .trailing, spacing: 4) {
986
-                Text(countText)
987
-                    .font(.subheadline.monospacedDigit())
988
-                    .foregroundStyle(countColor)
989
-                if isSignificantChange, let delta {
990
-                    SeverityBadge(delta: delta)
991
-                }
992
-            }
993
-        }
994
-        .accessibilityElement(children: .combine)
995
-    }
996
-}
997
-
998 544
 private struct SnapshotDataRangeIndicator: View {
999 545
     let oldestRecordDate: Date?
1000 546
     let newestRecordDate: Date?
@@ -1155,5 +701,5 @@ private struct ShareSheet: UIViewControllerRepresentable {
1155 701
             profile: LocalDeviceProfile(deviceID: "preview-device")
1156 702
         )
1157 703
     }
1158
-    .modelContainer(for: [HealthSnapshot.self, SnapshotDelta.self, TypeDelta.self, TypeCount.self, YearlyCount.self, HealthRecord.self, TypeDistributionBin.self], inMemory: true)
704
+    .modelContainer(for: [HealthSnapshot.self], inMemory: true)
1159 705
 }