If you are building an alarm app on iOS and you have found AlarmKit, you have probably had the same thought I did: finally, an actual alarm API. It breaks through Silent Mode and Focus the way the built-in Clock does, which no notification will ever reliably do.
I built a proof of concept and then threw it away. There is one button on the system alert that I cannot remove, and it happens to delete my entire product.
Then, three months later, I shipped it anyway. That part is at the end.
The same feature took an afternoon on Android and has worked ever since. That contrast is most of what this post is about.
What my alarm needs that a normal one does not
RYZA is not a noise at a time. The alarm shows a sentence, and to turn it off you have to read that sentence out loud. The app listens, decides whether you really said it, and only then stops.
The whole product is the second half of that. An alarm you can silence without speaking is a worse version of the Clock app that came free with the phone.
So the question was never “can AlarmKit make a sound at 6am.” It was whether AlarmKit could make a sound at 6am and refuse to stop until the challenge was done.
The test
29 May 2026, on an iPhone 17e simulator running iOS 26.5, Xcode 26.5. AlarmKit needs iOS 26 or newer, so this was always going to be a second code path rather than a replacement. I could live with that.
I added NSAlarmKitUsageDescription to Info.plist, wrote a small MethodChannel bridge
from Flutter into Swift, requested AlarmKit authorization, and scheduled one alarm ten
seconds out with sound: .default.
The alert got two buttons. The system stop button, which I do not control, and a custom one wired to an App Intenta small action the system can run inside your app on your behalf that was supposed to open RYZA straight into the quote challenge.
Four seconds
The banner appeared, branded as mine, looking exactly like a real alarm. Good.
Then I tapped the system stop button and the alarm went away. No challenge, no sentence, no speech. Over.
I tried it with the app open and closed on the Home Screen. Same both times.
Two other things did not work, and I suspect both are the simulator rather than AlarmKit: the custom button never opened the app, and the alarm made no sound at all. Neither is a fair test on a simulator and I have not repeated them on a physical iPhone. I am not claiming AlarmKit is silent or that its custom actions are broken. I do not know.
The stop button is different. That one is not an artifact. It is the documented design.
Apple is right, which does not help me
The session material is clear that people can stop or snooze an AlarmKit alert. Of course they can. An alarm framework that let apps trap users in an alert they could not silence would be a support catastrophe and a genuinely bad idea. Apple made the correct call.
That correct call removes my entire product.
I looked for a way around it. An App Intent can run code when the stop action fires, so in principle the app could notice the dismissal and reopen itself into the challenge. But by then the alert is gone and the sound has stopped. Running code after someone silenced the alarm is not the same as requiring them to finish something before it silences.
There is one architecture I could have shipped: AlarmKit purely to wake the phone, a custom “Start challenge” button on the alert, quote flow in the app after they tap it. That is a real product. It is not mine, because the plain stop button sits right next to mine and takes one tap.
Android hands you the whole thing
Here is the part that stings.
On Android this is a solved problem, solved by APIs that exist specifically for alarm
clocks. AlarmManager.setAlarmClock() schedules a real alarm that fires through Doze. A
full-screen intentan Android notification that opens your own screen instead of showing
a banner launches my activity over the lock screen. Audio marked USAGE_ALARM plays at
alarm volume regardless of the ringer. A foreground service keeps it alive while the user
reads.
The manifest entries are short and boring: USE_EXACT_ALARM, USE_FULL_SCREEN_INTENT,
WAKE_LOCK, RECEIVE_BOOT_COMPLETED. All declared, none prompting at runtime, none
requiring me to ask a platform owner for permission to build my product.
The difference is not that it was easier. It is what it lets you build. On Android the thing that appears at 6am is my screen, with no system alert in front of it. Android’s model is “wake the phone and hand control to the app.” AlarmKit’s model is “the system owns the alert.”
Both are defensible. Only one lets an app promise anything about how its alarm gets dismissed.
The door I was waiting on never opened
So I went back to the path I had: the native bridge on Android, the Flutter alarm
package on iOS. It kept the challenge in front of the dismissal, which is the part I care
about. What it could not do was break through Silent Mode.
For that I asked Apple for the Critical Alertsan Apple permission that lets an alert sound through Silent Mode and Focus entitlement. It would have given me the reliability while leaving the dismissal in my hands, which is exactly the combination AlarmKit refuses to sell. I sent the request with the AlarmKit result attached as evidence that I had evaluated Apple’s own framework first.
Then nothing. For two months, nothing at all. The answer, when it came in July, was no.
Time Sensitive notifications were the third option and I ruled them out early. They change priority and Focus behaviour, but they are not a promise that a sound will play through Silent Mode, and treating them as one would be lying to somebody who set an alarm for a flight.
That left me with no way to make an iPhone ring through Silent Mode except the framework I had already rejected.
I shipped it anyway
There is a version of this post where I hold the line, keep the pure architecture, and ship an alarm that stays quiet for anyone who sleeps with their phone on silent. I did not write that version, because I wanted the app out.
RYZA runs on AlarmKit on iOS now. The stop button is still there and I still cannot remove it.
What I could do was make stopping it insufficient. If the alert is dismissed without the voice challenge being completed, a guard alarm re-rings sixty seconds later, on the user’s own sound and title. Miss it again and it comes back again. The system alert gets the last word on any single ring, and I get the last word on the morning.
That is a worse mechanism than the one I designed. It is louder, it has more moving parts, and it turns a clean rule into a loop. It also works, and it works today rather than whenever Apple decides differently.
The compromise came with a bill attached. Guard alarms are independent of the alert that scheduled them, so for a while you could legitimately finish the challenge and still get ambushed sixty seconds later by a guard nobody cancelled. An alarm that does not believe you is worse than an alarm that is too quiet, and I had built exactly that. Stopping or handing off a mission now clears the related guards, best-effort, because a failed cleanup call should never block the handoff itself.
If I had known in May that the entitlement was going to take two months to say no, I would have started here. The proof of concept was not wasted, though. It is the reason I knew precisely which promise I was giving up.
What I would tell myself in May
Test the dismissal first.
Almost the whole day went into plumbing: usage description, bridge, authorization, scheduling. All of it worked. The thing that killed the integration was visible about four seconds after the first alert appeared, and I could have found it with a hardcoded alarm and no Flutter involved at all.
The wider lesson is about the waiting. On one platform I spent an afternoon writing code. On the other I spent an afternoon writing code, then wrote a document, then waited two months to be told no, then went back and used the thing I had rejected. Apple is entitled to that answer. I am not entitled to build my schedule around getting a different one.
Since shipping, the two layers have found new ways to disagree — most memorably after a device reboot, where AlarmKit remembered an alarm that the app could not yet read. The speech scoring that all of this exists to protect is a separate story.