Repository navigation
Count phone and watch app use in the daily analytics summaries - #2629
Merged
Merged
Conversation
Adds management-burden counts to the daily summary so time spent in the app and how often it has to be opened can be measured, using the same once-a-day, out-of-session event as the existing loop and glucose counts: - foregroundCount, foregroundSeconds, maxForegroundSeconds, and session lengths bucketed under 10 s, 10–60 s, 1–5 min and over 5 min, measured from scene foreground to background - touchCount: touch sequences in the app's window, from a gesture recognizer that fails immediately and never cancels or delays touches - notificationOpenCount, linkOpenCount (widget and Live Activity deep links) and alertAcknowledgementCount - screenCounts for the status, bolus, carb entry and settings screens - bolusCounts and carbEntryCounts by source (Phone, Watch, Remote) and overrideCounts by kind, with totals The new counts are stored as an optional nested value so a summary saved by an earlier build still decodes and the day in progress is kept. PresetActivationObserver is now @mainactor; AnalyticsServicesManager is its only conformer and the presets manager already calls it on the main actor.
The watch app now keeps its own daily usage summary and sends each completed day to the phone with transferUserInfo, which queues while the phone is out of reach; up to 14 unsent days are kept. The phone logs it as a "Watch daily summary" event (out of session) carrying the watch's own day: - watchForegroundCount, watchForegroundSeconds, maxWatchForegroundSeconds and the same session-length buckets as the phone, from applicationDidBecomeActive to applicationWillResignActive (wrist down ends a session) - watchComplicationLaunchCount: launches whose userInfo carries CLKLaunchedTimelineEntryDateKey - watchNotificationResponseCount - watchScreenCounts: carb entry, bolus, presets, carb list, and swipes to the actions, chart and sensor pages The phone's own daily summary adds watchContextRequestCount (the watch asks for context on activation and when each page first appears, so this runs a little above the number of opens), watchAlertAcknowledgementCount and watchPresetCount. The session-length bucketing moves into LoopCore as ForegroundSessions so the phone and the watch share it; the phone's summary keys are unchanged. The phone no longer asserts on userInfo transfers from the watch.
ps2
added a commit
that referenced
this pull request
Oct 31, 2026
ExtensionDelegate.shared() looked up WKApplication.shared().delegate, which is nil under the SwiftUI app lifecycle, so it trapped. The usage recording added in #2629 calls it from SwiftUI views, so swiping between pages, or opening carbs, bolus or presets, crashed the watch app. The delegate now records itself when it is created.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds app-use counts to the daily analytics summary as a measure of management burden: how often Loop has to be opened, for how long, and how much is done in it. They ride on the existing once-a-day, out-of-session "Daily summary" event (#2574), so no new events are sent and no health data is included.
New summary properties
foregroundCount,foregroundSeconds,maxForegroundSeconds, and session-length bucketsforegroundUnder10sCount,foreground10To60sCount,foreground1To5MinCount,foregroundOver5MinCount. A session runs from scene foreground to background, so Control Center or the notification shade don't split it.touchCount: touch sequences in the app's window. Counted by a gesture recognizer on the window that fails on the first touch and never cancels or delays touches.notificationOpenCount(default notification action),linkOpenCount(widget / Live Activity deep links),alertAcknowledgementCount(all acknowledgement paths: in-app, notification action, Watch, AlarmKit Stop).screenCountsfor status, bolus, carb entry and settings.bolusCounts/carbEntryCountsby source (Phone, Watch, Remote) andoverrideCountsby kind, each with a total (bolusCount,carbEntryCount,overrideCount).Watch
transferUserInfo(queued while the phone is out of reach; up to 14 unsent days kept). The phone logs it as a separate out-of-session "Watch daily summary" event with the watch's ownday:watchForegroundCount,watchForegroundSeconds,maxWatchForegroundSecondsand the same length buckets, fromapplicationDidBecomeActivetoapplicationWillResignActive(wrist down ends a session)watchComplicationLaunchCount(launch userInfo carriesCLKLaunchedTimelineEntryDateKey)watchNotificationResponseCountwatchScreenCounts: carbEntry, bolus, presets, carbList, and swipes to the actions / chart / sensor pageswatchContextRequestCount(the watch requests context on activation and when each page first appears, so this runs a little above the number of opens),watchAlertAcknowledgementCountandwatchPresetCount.ForegroundSessions) so both share it; the phone's keys are unchanged. The phone no longer asserts on userInfo transfers from the watch.Notes
PresetActivationObserveris now@MainActor;AnalyticsServicesManageris its only conformer andTemporaryPresetsManageralready calls it on the main actor.Testing
AnalyticsServicesManagerTests(6 tests: session bucketing, decoding a summary stored before this change, foreground/background with touches, background without foreground, action counts by source, watch counts) andWatchUsageRecorderTests(3 tests: a completed day is sent when the next starts, unsent days are kept until the session is available, userInfo round trip).