Showing 2 changed files with 17 additions and 9 deletions
+2 -1
HealthProbe/Services/HealthKitService.swift
@@ -912,6 +912,7 @@ final class HealthKitService {
912 912
         archiveObservationID: Int64,
913 913
         progress: SnapshotFetchProgress?
914 914
     ) async -> TypeCountFetchResult {
915
+        let dateFetchStartedAt = Date()
915 916
         let dateDeadline = Date().addingTimeInterval(timeoutSeconds)
916 917
         async let earliestTask = measureAPICall(
917 918
             queryType: "earliest_sample",
@@ -932,7 +933,7 @@ final class HealthKitService {
932 933
         let earliestResult = await earliestTask
933 934
         let latestResult = await latestTask
934 935
         var apiCalls = [earliestResult.apiCall, latestResult.apiCall]
935
-        let dateFetchElapsedSeconds = earliestResult.apiCall.elapsedSeconds + latestResult.apiCall.elapsedSeconds
936
+        let dateFetchElapsedSeconds = Date().timeIntervalSince(dateFetchStartedAt)
936 937
 
937 938
         guard earliestResult.apiCall.status == .complete, latestResult.apiCall.status == .complete else {
938 939
             let status = firstImpairedStatus(in: apiCalls)
+15 -8
HealthProbe/Views/Dashboard/DashboardView.swift
@@ -343,14 +343,21 @@ struct DashboardView: View {
343 343
             lines.append("Retry of:   \(retryOfSnapshotID.uuidString)")
344 344
         }
345 345
         lines.append("")
346
-        lines.append("INTERPRETATION_HINTS")
347
-        lines.append("Partial snapshot: \(isPartialSnapshot ? "true" : "false")")
348
-        lines.append("Requires user resolution: \(requiresResolution ? "true" : "false")")
349
-        lines.append("Failed metrics are excluded from checksum/change analysis")
350
-        lines.append("Do not infer deletion from partial snapshots")
351
-        lines.append("All-values-missing metrics require review before saving")
352
-        lines.append("Timeout means HealthProbe cancelled after configured timeout, not necessarily HealthKit denial")
346
+        lines.append("DIAGNOSTIC NOTES")
353 347
         lines.append("Per-metric timing sums are cumulative work and can exceed wall-clock duration because type fetches overlap")
348
+        lines.append("Fetch/processing/insert/finalize fields are measured for import performance comparison")
349
+        if isPartialSnapshot {
350
+            lines.append("Partial snapshot: true")
351
+            lines.append("Failed metrics are excluded from checksum/change analysis")
352
+            lines.append("Do not infer deletion from partial snapshots")
353
+        }
354
+        if requiresResolution {
355
+            lines.append("Requires user resolution: true")
356
+            lines.append("All-values-missing metrics require review before saving")
357
+        }
358
+        if progress.types.contains(where: { $0.apiCallDetails.contains(where: { $0.status == .timeout }) }) {
359
+            lines.append("Timeout means HealthProbe cancelled after configured timeout, not necessarily HealthKit denial")
360
+        }
354 361
         lines.append("")
355 362
         lines.append("CONFIGURATION")
356 363
         lines.append("adaptiveTimeoutsEnabled: \(progress.adaptiveTimeoutsEnabled ? "true" : "false")")
@@ -393,7 +400,7 @@ struct DashboardView: View {
393 400
         }
394 401
 
395 402
         lines.append("")
396
-        lines.append("HEALTHKIT API RESULTS")
403
+        lines.append("IMPORT PERFORMANCE BY METRIC")
397 404
         let orderedTypes = progress.types.sorted(by: { $0.displayName < $1.displayName })
398 405
         let degradedIDs = Set(degraded.map(\.id))
399 406
         let reportTypes: [SnapshotFetchProgress.TypeProgress]