Skip to content

Tidepool sync 2026-05-11#2438

Open
loopkitdev wants to merge 494 commits into
LoopKit:devfrom
loopkitdev:tidepool-sync/2026-05-11
Open

Tidepool sync 2026-05-11#2438
loopkitdev wants to merge 494 commits into
LoopKit:devfrom
loopkitdev:tidepool-sync/2026-05-11

Conversation

@loopkitdev
Copy link
Copy Markdown

Refreshed Tidepool → DIY sync from the tidepool-sync/2026-05-11 branch.

This supersedes and replaces the previous Tidepool Merge PR (#2237), which is being closed in favor of this one.

Camji55 and others added 30 commits December 12, 2024 10:35
…date UI (LoopKit#749)

* if delivery states are not changed, still update UI

* set refresh context before reloading
…ntifiers-for-new-test

[QAE-446] Add identifiers for new tests
* Preset editing

* Preset editing updates

* Add duration editing

* Add duration for legacy workout override
…ntifiers-for-UI-tests

[QAE-451] Add identifier for Presets button
ps2 and others added 20 commits April 30, 2026 13:08
… updates. (LoopKit#925)

* [LOOP-1114] Add blocking modal and open loop enforcement for required updates

When a support plugin returns .required from checkVersion, Loop now:
- Forces the app into open loop mode
- Sends a critical user notification
- Presents a non-dismissable modal with an App Store link

The modal and notification use the dynamic app name for white labeling.

* Clean up unneeded checks for pre iOS 15 support
…nce & Fix Status Icon Mismatch/Freshness Sync (LoopKit#924)

* [LOOP-5878] Remove Duplicate Reference

* [LOOP-5797] Fix Loop Status Icon Mismatch

* [LOOP-5771] Freshness Sync

* [LOOP-5771] Freshness Color Sync
…pKit#927)

* Updates to wording and layout tweak for Required Software Update

* Tweaks
14 commits from tidepool-org/Loop dev. Only the project.pbxproj had
conflicts (3 regions); all source files auto-merged.

pbxproj resolution:
- Region 260-264 / 1033-1037: kept both ContentMargin.swift (DIY) and
  PresetPerformanceHistoryView.swift (Tidepool) — additive
- Region 1251-1261: dropped Tidepool's *.strings PBXFileReferences
  (ru/de InfoPlist/Localizable/ckcomplication); kept Tidepool's new Swift
  files (RequiredVersionUpdateView, AutomationHistoryEntry,
  AutomationHistoryEntryTests, LoopCircleView)
- Removed 3 orphaned variant-group children at lines ~4088, 4147, 4166
  that referenced the dropped .strings FileReferences
# Conflicts:
#	Loop/Managers/Live Activity/LiveActivityManager.swift
Without this, automatic temp basal changes, no-op Loop cycles, and
manual temp basal cancellations never produce the
"updateRemoteRecommendation" dosing decision that NightscoutService
pairs with the cached "loop" decision to upload Loop pill + forecast.

Add a force parameter to updateRemoteRecommendation() that bypasses the
"manual bolus rec changed" gate, forwarded through updateDisplayState
as forceStoreRemoteRecommendation. Call updateDisplayState(force: true)
at the end of loop() (success and error branches) and cancelActiveTempBasal().

DIY divergence from tidepool/Loop. See memory/divergence_ns_post_dose_decision.md.
Original NS pairing logic introduced in NightscoutService commit 08abe274
(2022-07-18) assumed a separate post-dose decision; later refactoring
consolidated it into the "loop" decision, breaking the pairing for
non-bolus paths.
ResizeablePicker was the only cross-module use of LoopKitUI's
ResizeablePicker (which Tidepool keeps internal-only). Replacing it with
a standard Picker(.wheel) here means LoopKit no longer needs DIY-only
public modifiers on ResizeablePicker — eliminating a small but ongoing
DIY divergence in LoopKitUI.
Bug: lock-screen Live Activity preview was showing raw Swift debug
output like 'Optional(LoopKit.PresetSymbol(symbolType: ..., value:
"figure.outdoor.cycle"))' above the preset name, because
TemporaryScheduleOverride.getTitle() was interpolating preset.symbol
directly:

  return "\(preset.symbol) \(preset.name)"

preset.symbol is Optional<PresetSymbol> (a struct introduced by the
2026-03-10 tidepool sync — wraps an SF Symbol name with a tint), not a
String, so Swift's default Optional interpolation produced the debug
dump.

Fix:
- Add iconSystemSymbolName: String? to the Preset Codable struct in
  GlucoseActivityAttributes (with backward-compatible decoder).
- Replace getTitle() -> String with liveActivityTitleAndSymbol() ->
  (title, systemSymbolName?). Emoji symbols are folded inline into the
  title (they render fine as text); .systemImage symbols are returned as
  a separate SF Symbol name; .image (asset) symbols can't be loaded from
  the widget bundle so we render the name without an icon. Pre-meal
  preset also gets a fork.knife icon.
- In ChartView, render the preset label as
  Text(Image(systemName: iconSystemSymbolName)) + Text(" ") + Text(title)
  when a symbol is present, else just Text(title).
… y-axis labels

The chart's y-axis labels (175/150/125/100) render on the right side of
the plot, but the preset-name label (e.g. '🚴 Biking') was also at
top-trailing, so they crowded the same top-right corner. Switched the
ZStack alignment to .leading and updated the label padding from
trailing→leading so the preset sits in the empty top-left area.
Use .chartPlotStyle's overlay (alignment: .topTrailing) to render the
preset label inside the plot rectangle rather than via an outer ZStack.
The plot area excludes axis labels, so right-aligning inside it puts the
label flush with the right edge of the chart without overlapping the
y-axis numbers (175/150/125/100) that render along the trailing axis.

Removed the surrounding ZStack and the trailing/.top vs leading/.top
alignment juggling — not needed anymore.
Moved the 'Live activity' NavigationLink out of AlertManagementView and
into SettingsView's alertManagementSection, directly below the Alert
Management row. Both now share a Section using the LargeButton style.

The new Live Activity row uses 'rectangle.on.rectangle' as the icon and
the descriptive text 'Lock Screen, Dynamic Island, and CarPlay display'.

Rationale: Live Activity is an output/display concern, not an alert
permission concern, so users were unlikely to look for it inside Alert
Management. Surfacing it at the top level makes it discoverable.
…stic report

Both fields are implicitly-unwrapped optionals that are guaranteed to be
set by the time the diagnostic report runs; reflecting the Optional
wrappers produced opaque "Optional(...)" lines that obscured the
underlying state. Matches how the other manager fields above and below
these lines are rendered.
The carb-effect chart on the Carb Absorption screen and the Favorite Foods
insights screen lost their predicted carb-effect line in the Tidepool
stateless-algorithm refactor. `dynamicGlucoseEffects(from: end, ...)`
generated effects only from now+1h forward, but the chart's visible window
ends at roughly now+1h — so the two ranges only touched at a single point
and nothing rendered.

Widen the output sampling window to `from: start` at both call sites
(carb-absorption review and historical-charts data). The carb-absorption
model itself is unchanged; only the sample window grows to span the chart.

No dosing-path impact: the main Loop algorithm runs through LoopAlgorithm
(SPM), not these UI-only helpers.

Reported by @marionbarker in LoopKit/LoopWorkspace#213.
Marion's LoopKit#2399 (b6e8841) added submodule branch+SHA to the build-details
section of the diagnostic report. The script and BuildDetails.submodules
accessor survived the Tidepool merges, but the report-builder block itself
was relocated from DeviceDataManager.swift to LoopAppManager in Tidepool's
refactor, and the merge kept Tidepool's older shape — so the consumer of
.submodules was effectively dropped.

Port the original change into generateDiagnosticReport():
- drop the now-redundant Loop-submodule gitRevision/gitBranch lines
- rename workspaceGit{Revision,Branch} to "Workspace SHA/branch"
- append the submodule list (alphabetized, "name: branch, sha")
Adds an `external` case to the bolus event type so manually-entered doses
appear as "External Insulin" in the delivery log instead of being lumped
in with corrections. The event details screen gains a destructive Delete
button (with confirmation) that removes the dose via DoseStore.deleteDose
when the underlying DoseEntry is manually entered.
@loopkitdev loopkitdev mentioned this pull request May 20, 2026
# Conflicts:
#	Loop.xcodeproj/project.pbxproj
#	WatchApp/Info.plist
Pete Schwamb added 7 commits May 20, 2026 16:49
The dev merge resurrected the legacy WKWatchKitApp key alongside DIY's
modern WKApplication single-target watch app, causing 'WatchKit App
doesn't contain any WatchKit Extensions' on archive/device builds.
Keep WKApplication + dev's WKSupportsLiveActivityLaunchAttributeTypes;
remove WKWatchKitApp.
New "Apple Health" settings row pushes a detail page showing the write/share
authorization status (Allowed/Denied/Not Set) for glucose and insulin, with a
warning indicator when sharing is denied. Read authorization is intentionally
not reported by iOS, so the page explains that, notes Loop reads insulin data
from other apps, and points the user at the Health app to review or change
access (the app cannot re-prompt once a choice is made).

Exposes DeviceDataManager.healthKitSharingStatus(for:) so the Settings layer
can read per-type sharing authorization.
Adds FeatureFlags.doseDeletionEnabled (off by default). When enabled, the dose
details screen offers a Delete action for any bolus/basal dose, not just
external (manually-entered) ones. Deleting a Loop-recorded dose that still has
active insulin (within the ~6h window) shows an extra confirmation warning that
Loop may make up for the reduced active insulin by dosing more. External-dose
deletion is unchanged and still always available.
updateDisplayState only ever advanced lastManualBolus to a newer bolus, so a
deleted (or otherwise removed) bolus lingered in the status "Last Bolus" footer.
Now reflect the most recent user-entered bolus actually present in the store,
clearing/downgrading when it is gone, while still preserving a just-enacted
bolus the store may not have persisted yet.
The Intents.intentdefinition variant group listed all 26 locales as children, but
the sync's .strings cleanup dropped the PBXFileReference definitions for 22 of them
(es, ru, en, it, fr, de, zh-Hans, nl, nb, pl, ja, pt-BR, vi, da, sv, fi, ro, tr, he,
ar, sk, cs), leaving dangling children. Those locales' Intents.strings were never
compiled, so Apple flagged missing localized descriptions for the NewCarbEntry and
EnableOverridePreset custom intents across all of them. Re-add the file references
(reusing the IDs the variant group already points at); the strings now bundle for
every declared locale.
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.

8 participants