Skip to content

Fix alarm sound session activation failures in background#596

Draft
bjorkert wants to merge 3 commits intodevfrom
fix/alarm-audio-session-activation
Draft

Fix alarm sound session activation failures in background#596
bjorkert wants to merge 3 commits intodevfrom
fix/alarm-audio-session-activation

Conversation

@bjorkert
Copy link
Copy Markdown
Member

@bjorkert bjorkert commented Apr 9, 2026

Fixes #590Unable to play alarm: Session activation failed.

Background activation of a non-mixable .playback audio session is denied by iOS with cannotInterruptOthers (560557684) unless the app is already actively playing audio. With Silent Tune the silent loop holds a mixable session alive, so activation succeeds. With a Bluetooth-heartbeat refresh (Dexcom / RileyLink / Omnipod Dash) there's no warm session at the moment of alarm, so setActive(true) fails and the alarm is silent.

Changes

  • AlarmSound.swift — at the five setCategory call sites, choose options: based on app state:

    • foreground OR backgroundRefreshType == .silentTune[] (legacy aggressive interrupt — no behavioral change for those users)
    • background AND not Silent Tune → .duckOthers (mixable; permits activation)

    Centralized in a new sessionCategoryOptions() helper.

  • AlarmManager.sendNotification — accepts an optional soundFile: SoundFile?. When provided, the notification uses UNNotificationSound(named: "<soundFile>.caf") instead of .default.

  • Alarm.trigger() — passes the alarm's soundFile to the notification only when both playSound is true AND we're in the fail-prone state (background + no Silent Tune). The system-delivered notification then carries the user's configured alarm sound as an audible fallback in case the in-app player can't activate. In foreground / Silent Tune the notification keeps .default so it doesn't echo the in-app loop.

Use .duckOthers instead of empty options when configuring the audio
session for alarm playback. The empty options created a non-mixable
session that conflicted with the background silent audio player
(which uses .mixWithOthers), causing setActive(true) to fail with
"Session activation failed" when the app was in the background.
@bjorkert bjorkert marked this pull request as draft April 26, 2026 08:39
@bjorkert
Copy link
Copy Markdown
Member Author

Changed it to draft, needs more testing.

Limit the .duckOthers option to the only state where legacy options: []
fails: background without Silent Tune holding a mixable session alive.
In foreground or with Silent Tune, restore options: [] so the alarm
continues to dominate other audio with no behavioral change for those
users.

In that same fail-prone state, plumb the alarm's soundFile through
AlarmManager.sendNotification so the system-delivered notification
carries the user's configured alarm sound as an audible fallback. In
other states the notification keeps .default to avoid an echo with the
in-app AVAudioPlayer loop.
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