All projects

Waiker

The alarm you can't snooze. Squat to turn it off.

BusinessApp Store2026
View on the App Store

Problem

Snoozing is too easy. The snooze button lets you negotiate with yourself every morning, and you lose the start of your day before it has begun.

Solution

Waiker makes snoozing impossible to fake. To turn the alarm off, you have to stand up and do real squats, and the front camera counts each rep on the device using Apple Vision. By the time the alarm stops, you are already on your feet and awake. No video is ever saved or uploaded.

What it is

A squat-based smart alarm for iPhone. When the alarm goes off, you turn it off by doing camera-verified squats, counted on the device using Apple Vision.

Design process

01

General research

I started from a simple, honest problem. The snooze button is too easy, and it steals the start of your day. I looked at how alarm apps try to force you awake, and where they fall short.

02

Market validation

There is a real category of mission alarms, apps that make you do something before the alarm turns off. That told me people already want this, so I decided to build an original take on it and never lean on any one competitor.

03

Ideation

I chose squats as the mission, because they get you on your feet and truly awake. Then I worked out how the phone could actually see and count a squat with the front camera, and how the alarm should keep coming back instead of snoozing.

04

Testing

I tested the squat detection early, with real movement and with recorded videos, and I watched closely where it miscounted, missed reps, or counted on the way down.

05

Iteration

I retuned the detection again and again. I changed how deep a squat has to go, added a second check so a single glitch cannot fake a rep, and grew the test harness to seventeen scenarios plus real video replay.

06

Build

I built the whole app in SwiftUI, the alarm chain on AlarmKit, the camera mission on Apple Vision, the subscriptions on StoreKit, and a squat first dashboard with streaks, a heat map, and a weekly pace race.

07

Test

Before every release I ran adversarial reviews that hunted for failure modes, and I worked through a written checklist that covered audio routing, cold starts, force quitting, and the payment path.

08

Release

Waiker is live on the App Store. Version one shipped in September 2026, and I have kept iterating from my own screen recording feedback into version 1.1.

Technical breakdown

SwiftSwiftUISwiftDataApple VisionAVFoundationAlarmKitStoreKit 2UserNotificationsSwift ChartsCore Haptics

Waiker is a single native SwiftUI app for iPhone, around 57 Swift files. One app state coordinates small focused services, one for the alarm chain, one for notifications, one for the camera, and one that reads your body pose. The rep counting lives in a pure engine with no framework around it, so the very same code that runs in the app also runs in command line test harnesses. Everything stays on the device, and camera frames are read in memory and never saved or sent anywhere.

Architecture

Pure engines that compile into both the app and a test harness

The rep counter, the alarm scheduler, and the rival pace race are written as plain Swift, with no UI or system framework imported into them. Because of that, the exact same source compiles into the app and into small command line harnesses, so I can replay a squat scenario or a missed alarm hundreds of times a second without ever opening the simulator. If you want your core logic to be testable, keep SwiftUI and AVFoundation out of it, and let the app be a thin shell around engines that take inputs and return outputs.

On-device vision

Counting a squat by hip drop, not knee angle

The obvious way to detect a squat is the knee angle, but from a phone propped in front of you the knees are foreshortened and the angle is noisy. So I measure the vertical drop of the hips instead, normalised against your standing pose, which holds up far better as the camera distance and framing change. Apple Vision returns the body pose on the device, a state machine turns hip height into squeeze and stand phases, and no frame is ever stored or sent. The lesson is to measure the signal the camera sees cleanly, not the one that is anatomically tidy.

On-device vision

Stopping a single Vision spike from minting a rep

Real squats were being miscounted, both missed and double counted. I lowered the depth threshold from 0.30 to 0.24 and raised the stand threshold from 0.10 to 0.15, then required a raw depth reading to corroborate the smoothed one, so a single noisy frame cannot complete a rep on its own, and added a knee fusion gate to confirm you actually stood back up. The accuracy harness grew to seventeen scenarios and replays real recorded videos, so every tuning change is measured against the same cases rather than guessed.

Reliability

Serialising AlarmKit so an alarm never silently drops

Fire and forget calls into AlarmKit produced eleven confirmed reliability bugs, because the daemon's state and the app's state drifted apart. I rebuilt the sync to be serialised and debounced at 250 milliseconds, to schedule the new alarm chain before cancelling the old one so there is never a gap with nothing armed, and to reconcile against the system's own list of alarms as the source of truth. A chainVersion fingerprint forces a full re sync only when the structure actually changed. With a background system service, your own state is a cache and the OS is the database.

Platform gotcha

The camera was stealing the audio session

Turning on the capture session silenced the alarm on wired headphones, because AVCaptureSession reconfigured the shared audio session out from under the player. Setting automaticallyConfiguresApplicationAudioSession to false stops that, and the sound service now tracks the active sound and restarts it on an interruption end or a route change. If your audio dies the moment the camera starts, the capture session is almost always the cause.

Platform gotcha

AlarmKit schedules that pass in the simulator and fail on the phone

AlarmKit relative schedules worked in the simulator and failed on real hardware with error 0. So I stopped using relative entries, schedule fixed dates only, and trust the device plus an in app status card over anything the simulator claims. With brand new system frameworks, treat the simulator as a rough sketch and verify the real path on hardware before you trust it.

Product reuse

Reusing the alarm mission as an all-day workout

The camera mission was built for the alarm, but because it was a self contained engine and not something wired into the alarm screen, the same code runs a multi set squat workout with rest periods and no alarm sound. That reframed Waiker from a single moment at wake up into an all day movement tool at almost no extra cost. Self contained features pay you back later in ways you did not plan for.

App Review

Two rejections, and the exact fixes that cleared them

Apple rejected under 3.1.2, subscriptions need a EULA link, and under 5.1.1 point four, permission screens that direct the user. I added the standard Apple EULA link to the store description, and rewrote the pre permission screens to use a neutral Continue that always proceeds to the system dialog, with no skip link. Both are small, specific changes, and knowing the exact guideline number is what turns a rejection from a mystery into a checklist item.

Design honesty

An escape hatch that never counts as success

There has to be a way out if the camera genuinely cannot see you, but it must not become the easy button. A three second hold to dismiss ends the alarm, and it is logged and pointedly never counted as a completed mission, so the streaks and stats stay honest. When you gate a real behaviour behind a sensor, design the failure path as carefully as the happy path.

Links