Hello,
I'm developing an iPhone alarm app with AlarmKit. The alert's secondary "Snooze" button uses secondaryButtonBehavior: .custom. Its secondaryIntent is a LiveActivityIntent with supportedModes: .background and authenticationPolicy: .alwaysAllowed.
In perform(), the app saves an in-progress record, schedules a new fixed alarm with AlarmManager.shared.schedule(id:configuration:), cancels the original alarm only after scheduling succeeds, and then saves completion. If something fails partway, the original alarm may remain, with or without the newly scheduled one.
Simplified configuration (not a standalone sample):
let presentation = AlarmPresentation(
alert: AlarmPresentation.Alert(
title: "Alarm",
secondaryButton: AlarmButton(
text: "Snooze", textColor: .white, systemImageName: "repeat"),
secondaryButtonBehavior: .custom))
let attributes = AlarmAttributes<ExampleMetadata>(
presentation: presentation, tintColor: .indigo)
let configuration = AlarmManager.AlarmConfiguration(
schedule: .fixed(fireDate),
attributes: attributes,
stopIntent: StopIntent(id: alarmID),
secondaryIntent: SnoozeIntent(id: alarmID),
sound: .named(soundName))
What we observed
We deliberately injected an app-side error inside the intent, using two different test apps on a spare iPad:
Observations 1 and 2: a small standalone AlarmKit probe app (iPadOS 26.2.1, default alarm sound). It does not use our app's core logic.
Observation 3: a separate validation app that uses our app's core logic (iPadOS 26.7, bundled alarm sound).
Each condition was observed only once, so these are not reproducible causal claims.
Error before scheduling the new alarm (probe app, device locked): The original alarm kept sounding, and snooze could be tapped again. We recorded several intent entries after the first error, but we can't tell which were user taps and which were system redeliveries. The alarm read as .alerting until the system Stop control was used.
Error after the new alarm was scheduled successfully (probe app): The original kept sounding for about 15 seconds until system Stop. The new alarm remained and fired at its scheduled time about three minutes later.
Error before scheduling (validation app, device unlocked with the app in the foreground): The alert appeared as a compact banner. After the failed snooze, the app was terminated and relaunched as part of the test procedure, and a test-only control allowed one retry. After that, the banner controls were no longer available, and reads over the next few minutes still reported the original as .alerting. About two hours later, cancelling it with the app's OFF action succeeded. We can't separate the effects of the intent failure, foreground presentation, relaunch, and test-only retry.
About hardware buttons: I understand from the AlarmKit FAQ that a physical button stops the currently alerting alarm and that stopIntent is called on dismissal. In observation 3, a volume button was pressed after the banner controls were gone, and audio was no longer heard. Our logs did not capture a stop-intent execution, though that doesn't prove it wasn't invoked. I'm not asking about the general hardware-button behavior. My question is only whether that guidance also applies to an alarm in the state left by a failed custom intent (question 3 below).
Questions
When a custom secondary LiveActivityIntent.perform() throws, what behavior should an app expect for the original alarm's state and alert presentation? Are there documented limitations or differences between the locked and foreground (banner) presentations?
What's the recommended way to report an unsuccessful snooze? Should the intent propagate the error, or catch it and return .result() while tracking the failure in the app? If the original stays .alerting, is invoking the same secondary intent again for the same alarm ID supported, and what concurrency or redelivery assumptions should the app avoid?
If an alarm reads as .alerting but has no visible controls or audio after such a failure, which public APIs or user actions are recommended for recovery? Does the FAQ's physical-button and stopIntent guidance still apply in that state? How should an app choose among stop(id:), cancel(id:), or a new schedule without cancelling a valid future alarm?
I've read the AlarmKit documentation and the AlarmKit FAQ (https://developer.apple.com/forums/thread/797158), but couldn't find guidance on this failure case. Any advice is appreciated.
Thank you!