Skip to content

Daily analytics summary, settings state, and no health data in events - #2574

Merged
ps2 merged 3 commits into
next-devfrom
analytics-daily-summary-and-privacy
Oct 14, 2026
Merged

ps2 merged 3 commits into
next-devfrom
analytics-daily-summary-and-privacy

Conversation

@ps2

@ps2 ps2 commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Cleanup of the usage analytics Loop sends when a user picks Share Usage Data.

Daily summary instead of per-cycle events

"Loop success" made up about 78% of all events, and "CGM Fetch" fires on every reading. Both are replaced, along with the per-cycle "Loop error", by one "Daily summary" event per local day. Properties:

  • day (yyyy-MM-dd) and utcOffset (seconds)
  • loopSuccessCount, meanLoopDuration, maxLoopDuration
  • loopErrorCount and loopErrorCounts (by issue id)
  • cgmFetchCount
  • closedLoop, dosingStrategy, integralRetrospectiveCorrection, glucoseBasedPartialApplication (state at the day's last loop or reading)

Running totals are kept in UserDefaults. A day's summary is sent on the first loop or CGM reading of the next day, so the last day before a device stops is never reported.

Settings state: who has what turned on

On/off events alone can't tell how many people use a feature on a given day. Anyone who turned it on before tracking started, or whose event was lost, is invisible. So the state is reported three ways:

  • in every daily summary, which gives daily counts directly;
  • as user properties (Closed Loop, Dosing Strategy, Integral Retrospective Correction, Glucose Based Partial Application), sent at launch and whenever the state changes;
  • as change events: "Closed loop enabled change", "Dosing strategy change", "Algorithm experiment change" {experiment, enabled}.

Changes are detected on the next loop or CGM reading. This catches the experiment toggles, which are @AppStorage in the view, and both closed-loop toggle paths.

Pump Type and CGM Type are now also sent at launch. Before, they were only sent when a device was set up, before the analytics services were registered, so restored devices never reported them.

No health data in events

The Share Data screen promises usage data "does not include health data like glucose values or dosing amounts". To match that:

  • "Bolus set" no longer sends units.
  • "Carb entry created" no longer sends grams.
  • "Override Enacted" sends only kind (preMeal/custom/preset/activity), durationMinutes and activityType. It no longer sends preset names typed by the user, glucose targets or the sensitivity multiplier.
  • Loop error details (dates, error text) are no longer sent; only issue id counts are.

Events that had no callers

These now fire:

  • "Override Ended" (preset deactivated)
  • "Settings Screen"
  • the loop settings change events: max basal, max bolus, glucose safety limit, targets, therapy schedule time zone

Alerts: displayed, not scheduled

"Alert Issued" fired when an alert was scheduled, so expiry alerts issued days ahead were counted at pairing time even if they were later replaced or retracted. It is replaced by "Alert Displayed" {identifier, interruptionLevel, scheduled}:

  • Immediate alerts are reported when issued.
  • Delayed and repeating alerts are reported after a loop, once they have come due without being retracted. Only the latest issue of each identifier counts, because re-issuing replaces the scheduled notification.
  • Loop-not-running notifications that iOS delivered while Loop wasn't running are reported when Loop infers their delivery. They weren't reported before.

"Displayed" means the alert came due and wasn't retracted. Whether iOS actually showed it (permissions, Focus) isn't visible to Loop.

Device removal

Pump Type and CGM Type are set to "None" when the device is removed, so later events aren't attributed to it.

Session fix

The inSession parameters on the carb and bolus events were ignored or inverted. Phone entries are now in session; watch and remote entries are out of session.

Related: LoopKit/LoopSupport#33 (default sharing to Share Version Only) and LoopKit/LoopSupport#34 (re-send user properties when sharing is turned on).

Tested by building the LoopWorkspace scheme for simulator and device, plus AlertStoreTests (47 pass, including a new testLookupScheduledAlertsDue). Running on a test phone sending to a test endpoint.

ps2 added 3 commits October 13, 2026 01:40
- Replace the per-cycle "Loop success", "Loop error" and "CGM Fetch"
  events with one "Daily summary" per local day: loop success count and
  mean/max duration, loop error counts by issue id, CGM fetch count, the
  day's UTC offset, and the settings state (closed loop, dosing strategy,
  integral retrospective correction, glucose based partial application).
  Running totals are kept in UserDefaults and the summary is sent on the
  first loop or CGM reading of the next day.
- Send that settings state as user properties at launch and whenever it
  changes, with "Closed loop enabled change", "Dosing strategy change" and
  "Algorithm experiment change" events. Also send Pump Type and CGM Type
  at launch; they were only sent when a device was set up.
- Stop sending health data: drop bolus units and carb grams, and send only
  the kind of override (preMeal, custom, preset, activity), its duration
  and the activity type, not preset names, targets or sensitivity.
- Send "Override Ended" when a preset is deactivated, "Settings Screen"
  when Settings opens, and the loop settings change events, none of which
  had callers.
- Carb and bolus events from the phone are now in session; from the watch
  and remote commands they stay out of session. The inSession parameters
  were ignored or inverted.
- Set the Pump Type and CGM Type user properties to "None" when the
  device is removed, so later events aren't attributed to it.
- Add trigger (immediate, delayed, repeating) and delayMinutes to
  "Alert Issued". Expiry alerts are issued days ahead as delayed alerts,
  which looked like alerts shown at pairing time.
"Alert Issued" fired when an alert was scheduled, so expiry alerts
issued days ahead were counted at pairing time even if they were later
replaced or retracted. Replace it with "Alert Displayed" {identifier,
interruptionLevel, scheduled}:
- immediate alerts are reported when issued
- delayed and repeating alerts are reported after a loop once they have
  come due without being retracted; only the latest issue of each
  identifier counts, since re-issuing replaces the scheduled alert
- loop-not-running notifications that iOS delivered while Loop was not
  running are reported when Loop infers their delivery
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant