Skip to content

Release v7.1.0 - #754

Merged
marionbarker merged 26 commits into
mainfrom
release/v7.1.0
Sep 20, 2026
Merged

marionbarker merged 26 commits into
mainfrom
release/v7.1.0

Conversation

@marionbarker

Copy link
Copy Markdown
Collaborator

Release v7.1.0.

Merging this PR triggers the tagging workflow, which creates tag v7.1.0 from LOOP_FOLLOW_MARKETING_VERSION in Config.xcconfig.

⚠️ Use "Create a merge commit" (not squash or rebase) so main keeps the original feature-PR commits.

marionbarker and others added 26 commits July 25, 2026 08:51
The old chart only drew what the gated update calls pushed into it, but
the Swift Charts model reads the data arrays directly, so Graph Basal,
Graph Bolus, Graph Carbs and Graph Other Treatments were never
consulted. Skip the corresponding data during rebuild when a toggle is
off, and mark the chart dirty when a toggle changes so it redraws on
return to the home screen.

Restore two settings-change behaviors dropped in the migration:
changing the prediction style re-routes the stored predBGs again, and
toggling Show Yesterday's BG reloads the BG window so the overlay
updates right away instead of at the next scheduled fetch.
When the system cancels a touch (permission alert, incoming call, app
switch) the chart gestures never get their onEnded call, so pinch or
inspect state stays latched and blocks all panning and zooming until the
app is restarted. Clear leaked gesture state when a new touch begins,
reset it when the chart disappears, and rebuild the gesture attachments
on foregrounding while keeping the current viewport.
* Refactor OverrideActivationModal to simplify duration handling and make duration settings available for all overrides

* Fix duration handling in OverrideActivationModal to use zero for indefinite activation
LoopFollow was deduplicating Nightscout treatments by Trio's shared FPU ID, which hid the first scheduled entry from the graph. Deduplicate by event type and occurrence time as well, preserving distinct FPU entries while still collapsing true duplicates.
* Recover the Silent Tune keep-alive after audio interruptions

The silent audio session is the only background execution claim Silent Tune
has, so when another app interrupts it the process is suspended within
seconds and no app code runs again until iOS resumes it. Reactivation now
happens inside a UIApplication background task assertion, which grants
runtime independently of the audio claim, so the retry attempts actually
run instead of being frozen by suspension. Attempts are spread over about
18 seconds, which fits inside both the assertion and a BGAppRefreshTask
window, and playAudio confirms the player is really playing rather than
assuming play() worked.

The BGAppRefreshTask stays open until the restart resolves, so a failed
first attempt is retried within the same window. It also queues its
successor before doing any work, asks for the earliest possible window when
a restart fails, and cancels itself when the user is no longer on Silent
Tune. scheduleRefresh leaves a pending request alone when it would run at
least as soon, so repeated background transitions no longer push the check
further out.

Logging follows the same shape: quiet when the keep-alive is healthy, and
detailed once it is struggling. A first attempt success stays behind debug
logging, while recovery after a failure reports the attempts and elapsed
time, repeated failures with an unchanged error code are suppressed so a
long ladder cannot bury the log, and session errors are named instead of
being printed as raw four character codes. The task scheduler records one
line per lost runtime window with its length and which background alerts
fired, and the background and foreground transitions record the refresh
mode, Low Power Mode and Background App Refresh status.

* Recover on interruption without trusting player.isPlaying

Device logs show isPlaying still reporting true at the moment an interruption
is delivered, so the recovery entered on interruption began was skipped
before it took its background task assertion, leaving the app dependent on
iOS delivering interruption ended after all. Recovery no longer consults
isPlaying to decide whether to run. Reattempting against a player that is
genuinely playing is harmless, because the session is activated before the
player is replaced, so a failed attempt leaves a working player untouched.

Recovery on interruption began now waits a second before its first attempt.
A brief interrupter's interruption ended lands inside that window and
supersedes the work, so momentary blips stay as quiet as before, and work
that does run means the claim is really gone whatever isPlaying says. The
immediate background refresh request moved to that point as well, so it is
made once per real interruption instead of once per blip, and the assertion
expiring now arms it unconditionally since reaching expiration means the
claim was never re-established.

* Recover from route changes and background relaunches

Silent audio also stops when the audio route disappears and when media
services reset, and neither posts an interruption notification, so nothing
noticed. A day of device logs shows three silent deaths with zero
interruptions while the phone moved in and out of CarPlay. Route changes and
media services resets are now observed and recover through the same ladder.
Recovery runs for a route appearing or disappearing; a category change never
recovers, because playAudio sets the category itself and an alarm takes over
the session that way. Other reasons are logged so a later log can earn them a
recovery.

A process launched into the background by BGAppRefreshTask never runs the
backgrounding transition, so it had no interruption, route or media services
observers and no background alerts armed. Observers are now attached whenever
audio is restarted, and a background recovery arms the alerts, which also
clears any delivered notification the recovery has just made obsolete. The
task scheduler is kicked on recovery so the alerts are re-armed from the
moment runtime returns. Alerts are only armed while backgrounded, since the
task's work lands on the main queue and the app may have been opened in
between.

The runtime gap is measured against a monotonic clock, so a wall clock
correction cannot hide a stall, and a material difference between the two is
reported. A scheduler park that outlives the moment between a task firing and
its action rescheduling it is now reported with its duration.

* Tighten comments in the background keep-alive
)

* Add lines to graph and mini graph for this time prior days

- Add vertical dotted orange lines to main graph for "this time" on prior days when scrolling back
- Add vertical dotted orange lines to the mini graph for "this time" on prior days

* White space cleanup

- Removed unneeded empty lines

* Put prior-day time lines behind a Graph Settings toggle

The vertical dotted orange lines marking the current time on prior days
rendered unconditionally on both the main and mini graphs. Gate them on a
new "Show Prior Day Time Lines" toggle in Graph Settings, defaulting to
off, so they behave like Show Midnight Lines and the other time markers.

The new storage key is unset for existing users, which reads as false, so
the lines are hidden until explicitly enabled.

---------

Co-authored-by: Auggie Fisher <auggie.fisher@shopify.com>
* Add App Intents to enable/disable BG speech via Shortcuts

* Keep project signing settings on the shared team variable

* Run Speak BG intents on the main actor, check storage readiness, and use the Speak BG name

---------

Co-authored-by: Jonas Björkert <jonas@bjorkert.se>
Loop uploads pump.reservoir in only some device status records while an
Omnipod is below 50U. The last exact reading is kept per pump and reused
for 30 minutes when the field is missing, and the pump row shows an em
dash once it is older than that.

A missing reading reads as 50+U for pumps that report a volume only when
it is low, and for records that name no pump at all, which is how Trio
and iAPS upload. Pumps that name themselves and report a volume in every
record get the em dash instead.

A reading that arrives within 15 minutes of a pump first appearing can
still be the previous pod's, so it is not taken as evidence that the pod
is below its reporting limit.

latestPumpVolume is optional so an unknown volume cannot reach the
reservoir alarm as 50.
* Clarify remote command sent confirmation message

* Fix formatting by adding a newline at the end of RemoteCommandMessage.swift
Share Logs now bundles the notice file and the log files into a single zip archive before opening the share sheet. The archive is created with NSFileCoordinator, so no new dependency is needed. If zipping fails the files are shared uncompressed as before.
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This PR targets the main branch. We do not accept PRs directly to main — please retarget your PR to the dev branch instead.

@marionbarker
marionbarker merged commit 4a74b78 into main Sep 20, 2026
3 checks passed
@marionbarker
marionbarker deleted the release/v7.1.0 branch September 20, 2026 18:15
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.

5 participants