Conversation
Builds without the Critical Alerts entitlement raise urgent glucose alarms through AlarmKit on iOS 26, and the alarm was scheduled with no sound, so it always played AlarmKit's default tone regardless of the sound chosen in Loop. The notification path had been passing the configured sound all along; it just isn't what makes the noise on those builds. Pass the alert's sound by name. The bundled alarm sounds are IMA4 .caf files under 30 seconds at the bundle root, which are the same constraints a notification sound has, so AlarmKit can play them directly.
…reading CGMs deliver the live reading and then backfill the gap behind it, each as its own batch. evaluate() re-ran on the backfill, which carries a sample no newer than the one just evaluated, and re-decided the alert from it. Seen with the G7: a live reading raised an urgent low at 22:58:59 and the backfill of the same period retracted it at 22:59:00, which also stopped the in-process alarm audio one second after it started. Skip any batch whose newest sample is not newer than the last evaluated reading. Backfill still reaches the glucose store; it just cannot override an alert decision made on a newer reading. (cherry picked from commit 125a01b)
ps2
force-pushed
the
fix/alarmkit-configured-sound
branch
from
September 16, 2026 04:20
0dbd3c0 to
6072aed
Compare
…ritical Alerts On builds without the Critical Alerts entitlement, AlarmKit is the audible channel for urgent alarms, and whether the user has allowed alarms was not visible anywhere: the permissions screen showed a "Critical Alerts: On" row that the checker never populates on those builds, so it read On regardless. A user who dismissed the authorization prompt had no way to see that their urgent low would fire silently. Report AlarmManager's authorization state through the permissions checker as a new flag, show it as an "Alarms" row in place of the Critical Alerts row where the entitlement is absent, and badge the iOS Permissions entry when alarms are not allowed. The checker already re-checks on foreground, so the row updates on return from Settings. Not added to requiresRiskMitigation, so it does not raise the unsafe-permissions modal.
Replacing the row with Alarms hid the fact that Critical Alerts are absent altogether. Show it as "Not Available" beneath the Alarms row, with a link to instructions for requesting the entitlement from Apple. The link is a placeholder for a loopdocs page; the URL is a single constant to swap.
…tical Alerts On those builds AlarmKit is how an urgent low makes a sound, so alarms being off is as unsafe as Critical Alerts being off is elsewhere. Add it to the risk-mitigation set so it raises the status banner and the unsafe-permissions modal, with its own text and alert identifier. It takes precedence over the notification flags in the mapping, since it only exists where the entitlement is absent. The badge on the iOS Permissions entry now comes from showWarning like the other flags.
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.
Fixes #2481. Five commits: the AlarmKit alarm plays the configured sound, a CGM backfill can no longer retract a fresh alert, and the AlarmKit authorization state becomes visible in iOS Permissions.
Background
Libre 3+ glucose alarms on next-dev come from Loop's own
GlucoseAlertManager, which stamps the configured sound onto the alert and issues it at.critical. The notification path honours that —UserNotificationAlertSchedulerusescriticalSoundNamedfor the copy inLibrary/Sounds.Builds without the Critical Alerts entitlement (every DIY build;
criticalAlertsEnabled: falsein the reporter's Loop Report) can't deliver a true Critical Alert, soAlertManageris meant to raise these through AlarmKit on iOS 26, with the in-processCriticalAlertAudioPlayeras fallback.Why the reporter hears the default tone
AlarmKit authorization is requested during onboarding and again on every launch (
launchHomeScreen), and both are no-ops once the user has decided. A user who never sees a prompt has already allowed or dismissed it — and if it's not allowed,scheduleAlarm()returnsfalseand the AlarmKit path never runs. On such a build the only sound left is the downgradedcriticalSoundNamednotification, which iOS plays with the system default alert tone; that is the reporter's "Radial". Users in that state need Settings → Loop → Allow Alarms.1. Play the configured sound on the AlarmKit alarm
Once authorized, the alarm was scheduled with no sound and would play AlarmKit's default tone regardless of the Loop setting — the comment said the
.caffiles "aren't guaranteed AlarmKit-compatible". They are:afinfoshows every bundled sound is IMA4.caf, 0.8–8 s, at the bundle root, which is the same constraint a notification sound has. Passsound: .named(filename)..vibratehas no filename and stays.default.2. Don't re-evaluate alerts from a backfill older than the last reading
Found while testing: CGMs deliver the live reading and then backfill, each as its own
.newDatabatch, andevaluate(samples:)ran on both. With a G7:Retraction calls
criticalAlertAudioPlayer.stop(), so the alarm audio was cut off one second after it started. Skip any batch whose newest sample is not newer than the last evaluated reading; backfill still reaches the glucose store.3. Show AlarmKit authorization under iOS Permissions
Without the entitlement, whether the user has allowed alarms was invisible — worse, the permissions screen showed a "Critical Alerts: On" row that the checker never populates on those builds, so it read On regardless. A user who dismissed the prompt had no way to learn their urgent low would fire silently.
The checker now reports
AlarmManager's authorization state as a new flag; the screen shows an Alarms row where the entitlement is absent, keeps a Critical Alerts: Not Available row beneath it, and adds a How to request Critical Alerts link (a placeholder for the loopdocs walkthrough — one URL constant to swap), and the iOS Permissions entry gets the warning badge when alarms aren't allowed. The checker already re-checks on foreground, so it updates on return from Settings. Alarms being off is also added torequiresRiskMitigation, so it raises the red status banner and the unsafe-permissions modal with its own "Turn On Alarms" text — on these builds it's the audible channel, so it's as unsafe as Critical Alerts being off is elsewhere.Testing
Builds clean. Confirmed on hardware — iPhone 16 Pro, iOS 26, a DIY build without the entitlement (
criticalAlertsEnabled: false), AlarmKit allowed:.caffiles are AlarmKit-playable, and the caution in the old comment is retired.Still open, and not addressed here: whether a downgraded
criticalSoundNamednotification should instead useUNNotificationSound(named:)so the visual notification also carries the chosen sound. On these builds that would be a second channel sounding alongside the alarm.