My ADHD vs. the AlarmKit API
It’s no exaggeration to say that alarms run my life. Without setting alarms for literally everything, I would be reduced to a gibbering, suppurating, boneless puddle that barely qualifies as a human being. My wife would also leave me*.
But, wielding my alarms (and a little “confidence”), I’m a 10x superhuman capable of delivering at a Series A startup, raising 2 small children, and growing a nascent blogging business. So imagine my excitement when Apple announced at WWDC 2025 that they are going to expose system functionality to us mere mortals, enabling us to create timers and alarms of our own accord! Today, we’re going to look at the new AlarmKit API:
Sorry, what were we talking about again? Right on. AlarmKit. This post is pretty long, so it may be cut off in some email clients. Read the full article uninterrupted, on my website. Live Activity stuff and Dynamic Island malarkey are huge rabbit holes, so I will cover them another time, where I can give Widgets and ActivityKit the love they deserve. On with the show. How AlarmKit works under the hoodIf you’ve ever suffered from sleep paralysis, AlarmKit is largely powered by the same thing under the hood: Daemons.
Daemons are system programs that run in the background. On iOS, they power core services such as the home screen (SpringBoard), touch & motion events from hardware (backboardd), audio & video playback (mediaserverd), and location services (locationd). A daemon also powers iOS alarms.
The basic underlying functionality in the Clock app is provided by MobileTimer.framework. This framework contains the full functionality powering timers and alarms. When using classdump-dyld or RuntimeBrowser, the full header interface for the alarms can be generated into a header file, Alarm.h. The interface is very similar to the AlarmKit scheduling parameters we’re going to look at soon, so it’s a fairly clear indication that Clock and AlarmKit are both built on top of this framework. So I think we are okay to use them interchangeably going forward. The Clock app has a private entitlement, com.apple.private.mobiletimerd, which grants it permissions to communicate with the mobile timer daemon (which itself links and imports MobileTimer.framework). This privileged program runs constantly in the background, monitoring alarms set up by the framework, scheduling them, and triggering them by calling _fireScheduledAlarm().
When fired, the alarm tells SpringBoard to interrupt whatever’s happening, and present the alarm UI. Alarms have special system privileges:
We can learn a lot about private APIs by lurking on Jailbreak forums. The BetterAlarm app demonstrates how it creates an alternative alarm screen interface by hooking into mediaserverd to intercept the alarm playback and presenting an alternative experience from SpringBoard. Now we have an idea what it’s doing under the hood, let’s look at how to actually use AlarmKit. AuthorizationLike all system capabilities, we need to set up permissions. It works much the same way as permissions for hardware like push notifications, camera, and microphone. First we set up the usage description in the Info.plist, the NSAlarmKitUsageDescription. Then in your code, request authorisation from AlarmManager. When called, we get a standard system dialog (plus my spicy flavour text). Erm, well that’s the really uninteresting bit out of the way. Let’s make some alarms. Scheduling AlarmsSetting up the first alarm is basically a bit of boilerplate, but then becomes very configurable once you power through. Fixed AlarmsAlarm.Schedule defines when the alarm triggers. Fixed schedules trigger at a specific timestamp in the future, ignoring timezones. This goes off 3 seconds after calling this function. It’s good for testing. My attention span isn’t that bad. Relative AlarmsRelative alarm schedules are better for my use case, since we can set them for specific dates and times, with repetition. If your timezone changes, these will adjust accordingly. I can use this schedule to ensure I don’t get distracted by my cats and miss my 10am standup. I told it to run weekly on the days and times given. Scheduling the AlarmTo actually set up the alarm on this schedule, we need to create an AlarmConfiguration object and pass this into the AlarmManager.schedule() method. Building and running the app, we see the configuration we created in a full-screen interrupt that displays after 3 seconds: Frankly, the API is a little weird, in that everything kind of nests inside everything else, which is simple but very verbose.
The ADHDMetadata is used to define stuff like icons on a Live Activity or configurable attributes. For now, you don’t really need to do anything other than define an empty structure conforming to AlarmMetadata to set up the AlarmAttributes.
Countdown TimersAs well as traditional alarms, we can set up one-off timers. You can do this with the AlarmConfiguration(countdownDuration:) form, or the AlarmConfiguration.timer() form. These work pretty similarly, with the countdownDuration allowing for a configurable snooze duration: The countdownDuration form of Alarm configuration sets the alarm screen at a time interval of preAlert seconds from now. Ten seconds after running the app, we get this bad boy interrupting our screen: Tapping “I forgot” causes the timer to snooze, and the screen presents again after the postAlert duration, again and again until you remember to take your meds. Configuring this postAlert will be fantastic for our spammy alarms. But let’s not get sidetracked. We don’t massively care about Live Activity configurations and countdowns. I’m not a cook. But I have been cooking. The CEO of my LifeThanks to this initial experimentation, I was able to kickstart some back-and-forth with our Claude and saviour, and now I have the first inklings of my ultimate ADHD Alarm life-management app. We pull together all the customisation options within AlarmKit, but focusing on the most important usability options missing in the vanilla Clock app:
Instead of relying on the AlarmKit API alarms property, we store everything in SwiftData and list them. This allows us to easily re-open and easily edit the individual alarms, as well as set names for them. Last OrdersI was deeply happy when this API finally came out*.
The AlarmKit API is almost certainly powered by the same engine which powers system Alarms from the Clock app: MobileTimer.framework and the mobiletimerd daemon. AlarmKit brings a fresh wealth of customisation to a system permission which, outside jailbreaks, has been locked down since the first iPhone. But, AlarmKit also represents a trend in Apple APIs that started with Widgets, became even more restrictive with Dynamic Island, and now exists in an even more egregious form in the AlarmKit UI. A trend where we get less and less customisation options, with highly restrictive and somewhat clunky APIs. The very simple UI offers very limited options, however we are still forced to wrap 5 layers of different structs from AlarmButton to AlarmManager.schedule() to make it work. But, I suppose, when we’re dealing with an interrupting system thing, strict restrictions are probably a necessary evil to prevent abuse. Check out the open-source code for ADHDAlarms here! Once iOS 26 is live, I’ll post it to the app store for free.
|