What AlarmKit Does Not Tell You
Apple shipped AlarmKit in iOS 26. It gives a third-party app the same delivery path the Clock app uses: the alarm rings through the ringer switch, through Focus, full screen on the Lock Screen. Before it, a third-party alarm app had one mechanism available — a local notification — and the ringer switch could silence it. That is the whole old problem in one sentence. You could schedule perfectly, and if the switch on the side of the phone was flipped, nothing made a sound.
The documentation covers the API well. Requesting authorization, scheduling an alarm, wiring up Stop and Snooze actions — it is all there and it works.
What follows is the part we only learned by shipping an alarm app and living with it for a few weeks. None of it is a complaint about the API. It is a set of things that read as unremarkable in the documentation and turned out to matter more than anything we wrote.
1. The moment you schedule it through AlarmKit, it stops being your event
We wanted to warn people before the alarm that mattered. Critical Mode has a pre-alert: ten or thirty minutes before the real alarm, tell the user it is coming. We scheduled it through AlarmKit, the same as the main alarm, because from inside the code both are the same shape — a thing that happens at a time.
To the user they are not remotely the same thing.
Anything scheduled through AlarmKit is an alarm. It rings through Silent mode. It presents the system alarm UI. What we built as “let you know the alarm is coming in thirty minutes” arrived as “an alarm I never set went off thirty minutes early.”
A user hit exactly that. They assumed they had misconfigured something and went into the app to turn it off.
There was no setting to turn off. We had not built one, because from our side it was not a feature — it was a helpful detail inside Critical Mode.
We split it out. Early alerts is now its own setting with 30 & 10 min, 10 min and Off.
The lesson generalises past this one bug: when you hand something to AlarmKit, it stops being the event you designed and becomes the alarm the user experiences. Whatever internal category you had for it does not survive the handoff. If you would not be comfortable with a user describing it as “an alarm went off”, do not schedule it as one.
2. Stop is a boundary, not a button
A pre-alert has a Stop button. The main alarm has a Stop button. They look identical and they arrive in your code through the same App Intent.
Our early implementation had one condition that failed to distinguish which kind of alarm it was handling. So pressing Stop on a pre-alert did not just end the pre-alert — it entered the screen the main alarm uses, and started the mission.
On a single-date alarm, finishing that mission could then cancel the alarm the user actually cared about. They would have completed a mission at 5:30am and lost the 6:00am alarm in the process.
In the diff it is a small conditional. In an alarm app, Stop is not a UI event. Handling it means answering several questions at once:
- What exactly was stopped?
- Should the next occurrence survive this?
- Is this the alarm the user set, or something the app scheduled around it?
The API gives you one entry point. The distinctions are yours to carry, and nothing in the signature reminds you that they exist.
3. The permission call is easy. The moment you make it is not
AlarmKit can let the system request authorization on its own when the first alarm is created. As API behaviour that is convenient, and it is the path of least resistance.
As product behaviour it puts the prompt in the worst possible place. The user picks a time, chooses repeat days, sets up a mission, hits save — and then a system dialog appears asking for something they have no context for. They decide based on a few lines of system text, at the moment they thought they were finished.
So the question we actually had to answer was not which authorization call to use. It was: what does the user already understand at the instant that prompt appears? Everything upstream of the prompt is doing the work.
What the pre-alert incident changed about the whole app
We did not start out believing any of this had to be on screen. The feature worked; that seemed like enough.
What the pre-alert taught us is that hiding an implementation difference is not the same as keeping the interface clean. It creates an expectation the app cannot meet, and the user pays for it at an hour when they have no patience for debugging.
So we went the other way. Where a difference changes what a user can reasonably expect, it now appears on screen, even if only as a short label.
The clearest case is delivery path. AlarmK asks for two permissions. With the system alarm (AlarmKit) permission granted, alarms ring through the Clock app’s path — through Silent, through Focus, full screen on the Lock Screen. With it denied and notifications allowed, you still have an alarm, and it is still scheduled, and it will still fire — but the ringer switch can silence it.
In your own UI those two look identical. The easy choice is to let them stay identical.
We label the second one “Scheduled via notifications” in the alarm list.
That label is not there to explain the architecture. Nobody needs to know which API scheduled their alarm. It is there to tell the user the range of behaviour they are entitled to expect, the night before rather than the morning after.
The same rule runs through the rest of the app. The ringing escalation setting only touches our own audio; it never works around an iOS setting. Once you start telling users what the system is doing, you have to be right about all of it.
Requiring iOS 26
AlarmKit does not exist before iOS 26, and there is nothing to fall back to.
That left two options: ship on older versions with notifications as the only path and accept that some alarms will not make a sound, or require iOS 26 and give up everyone who has not updated.
We require iOS 26. An alarm that might stay quiet is worse than an app you cannot install, because the second failure happens in the App Store and the first one happens in bed.
Reasonable teams would choose differently, and the cost is real. But if the promise is reliability, shipping a tier that cannot deliver it is a strange thing to do for install numbers.
One gap worth knowing about
AlarmKit does not support watchOS. iPhone alarms mirror to the wrist under the system’s own rules and the Live Activity appears in the Watch Smart Stack, but there is no standalone Watch alarm.
For most apps that is a footnote. For this one it constrains the core mechanic: an AlarmK alarm stops when you finish a mission — scan a QR code across the room, solve a problem, do squats — and the mission has to be finished on the iPhone. A wrist-only flow is not available to build.
If you are about to start
The documentation tells you what happens when you call the code.
It does not tell you what that will mean to someone at six in the morning.
That gap is where all three of the problems above came from, and it is not a gap Apple can close for you. Budget for it.
AlarmK is a free mission alarm app for iPhone, built by Eazler. An alarm stops when you finish one of eight missions — a QR or barcode scan, a math problem, memory, typing, a shake, squats, a walk or a photo. Up to five can be chained on one alarm. No in-app purchases, no ads, no account. iOS 26 or later.
Written for AlarmK 1.3.1. Published 21 September 2026.