Showing 3 changed files with 49 additions and 1 deletions
+1 -0
HealthProbe/Doc/04-project/Import-Optimization-Log.md
@@ -575,6 +575,7 @@ rows exist".
575 575
 | 2026-06-03 | `e231eaf` | Use the HealthKit registry as SQLite sample type display-name fallback. | Triggered by Snapshot detail showing raw identifiers such as `HKCategoryTypeIdentifierAppleStandHour` after UI moved from Core Data cache to SQLite summaries. Expected signal: existing and new archive rows show human-readable names such as `Stand Hours` without requiring reset/reimport. |
576 576
 | 2026-06-03 | `5fafcdd` | Expand the HealthKit type registry for full-dataset discovery while keeping the original 15-type profile as the tested default. | Triggered by the decision that import/storage cannot be considered complete based only on the restricted v1 dataset. Expected signal: Settings/authorization can expose a much broader quantity/category/workout catalog, unsupported types are explicit, and real-device coverage reports can measure full authorized backup volume. |
577 577
 | 2026-06-03 | committed | Add explicit capture profile controls for full-dataset discovery. | A real-device report after registry expansion still showed `Types: 15/15 processed` and the old monitored type-set hash because `selectedTypeIDs` persisted the v1 core profile in `UserDefaults`. Settings now exposes `Select All Available Types`, `Select Core Profile`, and selected/available counts so the next real-device run can deliberately switch from the v1 sample set to the expanded supported registry. |
578
+| 2026-06-03 | pending | Migrate legacy core-profile selections to full available capture by default. | A follow-up real-device report still showed the old `4907...` monitored type-set hash and `Types: 15/15 processed`, proving the running app still used the old persisted selected type set. New installs and pre-profile settings that exactly match the old core profile now migrate to `All available`; only an explicit `Select Core Profile` action persists the core subset. Settings also shows the active profile label (`All available`, `Core`, or `Custom`) for quick verification before capture. |
578 579
 
579 580
 ## Current Diagnosis
580 581
 
+43 -1
HealthProbe/Utilities/AppSettings.swift
@@ -3,11 +3,18 @@ import Foundation
3 3
 @Observable
4 4
 final class AppSettings {
5 5
     private static let selectedTypeIDsKey   = "hp_selectedTypeIDs"
6
+    private static let captureProfileKey = "hp_captureProfile"
6 7
     static let adaptiveTimeoutsEnabledKey = "hp_adaptiveTimeoutsEnabled"
7 8
     static let simplifiedUIModeEnabledKey = "hp_simplifiedUIModeEnabled"
8 9
     static let typeDetailCacheBackfillVersionKey = "hp_typeDetailCacheBackfillVersion"
9 10
     static let currentTypeDetailCacheBackfillVersion = 2
10 11
 
12
+    private enum CaptureProfile: String {
13
+        case allAvailable
14
+        case core
15
+        case custom
16
+    }
17
+
11 18
     var selectedTypeIDs: Set<String> {
12 19
         didSet { persistTypes() }
13 20
     }
@@ -44,6 +51,12 @@ final class AppSettings {
44 51
         selectedTypeIDs == Self.availableTypeIDs
45 52
     }
46 53
 
54
+    var captureProfileDescription: String {
55
+        if usesAllAvailableTypeProfile { return "All available" }
56
+        if usesDefaultTypeProfile { return "Core" }
57
+        return "Custom"
58
+    }
59
+
47 60
     static var currentDeviceID: String {
48 61
         KeychainService.resolveDeviceID(swiftDataStoreIsEmpty: false).id
49 62
     }
@@ -59,11 +72,32 @@ final class AppSettings {
59 72
     }
60 73
 
61 74
     init() {
75
+        let persistedTypeIDs: Set<String>?
62 76
         if let data = UserDefaults.standard.data(forKey: Self.selectedTypeIDsKey),
63 77
            let ids  = try? JSONDecoder().decode([String].self, from: data) {
64
-            selectedTypeIDs = Set(ids)
78
+            persistedTypeIDs = Set(ids)
65 79
         } else {
80
+            persistedTypeIDs = nil
81
+        }
82
+
83
+        let persistedProfile = UserDefaults.standard.string(forKey: Self.captureProfileKey)
84
+            .flatMap(CaptureProfile.init(rawValue:))
85
+
86
+        switch persistedProfile {
87
+        case .allAvailable:
88
+            selectedTypeIDs = Self.availableTypeIDs
89
+        case .core:
66 90
             selectedTypeIDs = Self.defaultTypeIDs
91
+        case .custom:
92
+            selectedTypeIDs = persistedTypeIDs ?? Self.availableTypeIDs
93
+        case nil:
94
+            if let persistedTypeIDs, persistedTypeIDs != Self.defaultTypeIDs {
95
+                selectedTypeIDs = persistedTypeIDs
96
+                Self.persistCaptureProfile(.custom)
97
+            } else {
98
+                selectedTypeIDs = Self.availableTypeIDs
99
+                Self.persistCaptureProfile(.allAvailable)
100
+            }
67 101
         }
68 102
 
69 103
         if UserDefaults.standard.object(forKey: Self.adaptiveTimeoutsEnabledKey) == nil {
@@ -78,19 +112,23 @@ final class AppSettings {
78 112
     func isEnabled(_ type: MonitoredType) -> Bool { selectedTypeIDs.contains(type.id) }
79 113
 
80 114
     func toggle(_ type: MonitoredType) {
115
+        Self.persistCaptureProfile(.custom)
81 116
         if selectedTypeIDs.contains(type.id) { selectedTypeIDs.remove(type.id) }
82 117
         else { selectedTypeIDs.insert(type.id) }
83 118
     }
84 119
 
85 120
     func selectDefaultTypes() {
121
+        Self.persistCaptureProfile(.core)
86 122
         selectedTypeIDs = Self.defaultTypeIDs
87 123
     }
88 124
 
89 125
     func selectAllAvailableTypes() {
126
+        Self.persistCaptureProfile(.allAvailable)
90 127
         selectedTypeIDs = Self.availableTypeIDs
91 128
     }
92 129
 
93 130
     func clearSelectedTypes() {
131
+        Self.persistCaptureProfile(.custom)
94 132
         selectedTypeIDs = []
95 133
     }
96 134
 
@@ -98,4 +136,8 @@ final class AppSettings {
98 136
         UserDefaults.standard.set(try? JSONEncoder().encode(Array(selectedTypeIDs)),
99 137
                                   forKey: Self.selectedTypeIDsKey)
100 138
     }
139
+
140
+    private static func persistCaptureProfile(_ profile: CaptureProfile) {
141
+        UserDefaults.standard.set(profile.rawValue, forKey: Self.captureProfileKey)
142
+    }
101 143
 }
+5 -0
HealthProbe/Views/Settings/SettingsView.swift
@@ -166,6 +166,11 @@ struct SettingsView: View {
166 166
     @ViewBuilder
167 167
     private var typeSelectionSections: some View {
168 168
         Section("Capture Profile") {
169
+            InfoRow(label: "Profile") {
170
+                Text(appSettings.captureProfileDescription)
171
+                    .foregroundStyle(.secondary)
172
+            }
173
+
169 174
             InfoRow(label: "Selected Types") {
170 175
                 Text("\(appSettings.selectedTypeCount) of \(appSettings.availableTypeCount) available")
171 176
                     .foregroundStyle(.secondary)