Five phone screens on a pale lavender background. Two habit lists with coloured cards, an iPhone screen with a red lock and the word Locked, a Screen Time settings page showing Locked, and a statistics page with a five week calendar grid

Web + iOS appSolo designer and engineerDrawing 39 of 43

Habits and
Screen Blocker

Habits is a habit tracker on my own site, and Blocker is a small iPhone app that reads it. When a habit with a lock is late, Blocker uses Screen Time to lock the apps I picked until I do it.

Sheet 02 of 09/The idea

Habits that unlock my phone

Habits lives behind the login on this site and saves to my server. Blocker is the iPhone side. It asks the server one question, locked or not, and applies the answer with Screen Time. I put all the logic on the server side, so the phone app stays a switch.

Every screenshot on this page uses made-up demo habits. The Today list shows a strip of days, the habits grouped, and a small lock on each one that can lock my apps. A late habit gets a red outline. Done, skipped and failed habits drop under a line at the bottom.

A phone habit list titled Today with a strip of days across the top, grouped cards for Health, Evening and Other, a red outlined Water card marked Late, and solid orange and green done cards below a divider
PL 02The Today list on demo habits at 8:10 PM. Water is late, so its card gets a red outline and it locks the apps. Done and skipped habits sit under the line at the bottom.
The same habit list on a black background with darker tinted cards and the late Water card outlined in red
PL 03The same demo day in the dark theme. The small lock next to a name means that habit can lock my apps.
The habit list for Mon, Sep 28 with two solid red cards reading Failed and Slipped among orange, green, yellow and teal done cards
PL 04A past demo day, Monday the 28th. A failed habit and a slipped negative habit both turn solid red with an X, and past days can still be filled in.

Sheet 03 of 09/Setup

Setting up a habit

I styled the app after Grit, another habit tracker. A habit gets a name, a colour, how many times a day, how it repeats and a complete by time for each tick. The Screen Time block sets when a missed one locks my apps. New habits start on Each time, so the apps lock only once a tick is late.

A negative habit is one I'm trying to stop. Tapping it means I slipped, which resets the streak and locks the apps for the rest of the habit day. A slip in the last 4 hours of the day locks all of the next day too.

An Edit habit sheet for Read 20 pages with a row of colour swatches, a times per day stepper, a group field reading Evening, a Negative habit switch and a Repeat control set to Every day
PL 05Editing a demo habit. Name, colour, times per day, group, the negative switch and how it repeats.
The lower half of the Edit habit sheet with a Complete by time of 10:00 PM, a Lock my apps switch, a Lock starts control set to Each time, a Skipping unlocks switch and a red Delete habit button
PL 06Lower in the same sheet. Each tick gets a complete by time, and the Screen Time block decides when a missed one locks the apps.
A New habit sheet named Snooze the alarm with the Negative habit switch on and a note reading Tap it when you slip. The streak resets and apps lock for the rest of the day
PL 08A new negative habit, a demo one called Snooze the alarm. The sheet drops the times and says what a slip does.

Sheet 04 of 09/Day to day

Logging the day

Tapping the ring adds one, and a tap past the target resets it. Holding a card opens a menu with skip, fail, complete, notes and the rest. The day starts at a time I choose, so a late night still counts for the day before. My Gym app also ticks any habit named Gym once a training day is complete.

A frosted pop up menu over the habit list with buttons for Skip, Fail, Complete, Duplicate, Add Note, Reset History, Edit, Archive and Delete, and rows for Statistics and More
PL 09Holding a card, or right clicking it on a computer, opens this menu.
A Note sheet at the bottom of the screen headed Gym, Today with the text Leg day. Squats felt easy today
PL 10A note on a demo habit for today. A small page icon on the card shows that a day has a note.
A grouped settings list with rows for Appearance, Theme, App Icon, Sort, Progress View, Badges, Day Starts At 04:00 AM, Week Starts On Monday, Screen Time Locked and Allow Future Dates
PL 17Settings, laid out like an iPhone settings list. With the day starting at 4:00 AM, a check in at 1:30 AM still counts for the day before.

Sheet 05 of 09/Progress

Streaks and statistics

Each card can show a Grid, Bars or Line view of its history, and it's off by default. The Statistics tab has the overall streak, a five week calendar and 7 and 30 day rates for every habit.

Habit cards each holding a small grid of coloured squares by week under month labels Aug and Sep
PL 11The Grid progress view on each demo card. The demo history only starts in September, so most of the grid is still empty.
Habit cards each holding ten rounded bars labelled 23 to 2, some partly filled, one hatched and some marked with an X
PL 12The Bars view, ten days per card. Hatched bars are skips and an X marks a fail or a slip.
Habit cards each holding a filled line chart that dips on missed days, with axis labels 19, 26 and 2
PL 13The Line view of the same demo history. The progress view is off by default and set from the view menu or Settings.
A Statistics page with tiles for Current streak 3 days, Best streak 4 days and Today 3/7, a five week calendar grid shaded in indigo, and cards for Water and Gym with percentages
PL 14The Statistics tab on demo data. Streaks, a five week calendar where solid means every habit was done, then a card per habit.
A statistics page for No phone after 11pm with streak tiles and a five week calendar where most days are solid indigo and four days are pale red
PL 15One demo habit's page. For a negative habit a solid day is a clean day and the red days are slips.
The Statistics page on a black background with bright violet calendar cells and two striped grey cells for the 19th and 20th
PL 16The Statistics tab in the dark theme. The two striped days are a demo vacation, and vacation days never break a streak.

Sheet 06 of 09/The rule

One rule file for both sides

The rule lives in one plain JavaScript file, rules.js. The page loads it as a script, and the server loads the same file, so the page and the phone can't disagree. GET /api/blocker/state answers with locked or not, the reasons, the moments in the next 8 days when that changes, and the complete by times for the next 48 hours. The phone sends a device key, and the server hashes both keys before it compares them. If the habits file is damaged, the server answers 503 so the phone keeps its last state.

While a habit locks my apps, the page refuses any edit that would stop it locking, like switching the lock off or skipping it. The one way out is doing the habit. Lock Now, the switch that locks everything by hand, can always be turned off.

Saving works differently from my other apps. A normal merge of two copies would bring back an untick, so each change is saved as a small operation that sets one value, and unsaved ones replay on top of the server copy after a conflict. rules.test.mjs runs 267 checks on the rule, including daylight saving changes, and they all pass.

The Edit habit sheet for Water with red text across the top reading Apps are locked. Finish your late habits first
PL 07Trying to switch the lock off on the late demo Water habit. The page keeps the sheet open and refuses the save.
A Screen Time page with a large red lock badge reading Locked, 3 of 7 habits done today, a Next Change row reading Unlocks tomorrow, a Why list naming Water 3/4, a Lock Now switch and Blocker App rows for Sync Phone and Change Blocked Apps
PL 18The Screen Time page with demo habits. It runs the same rule file as the server, so it shows what the phone is told, why, and when that changes next.
The Screen Time page on a black background with the red Locked badge, the Water reason, the Lock Now switch and the two Blocker App rows
PL 19The same page in the dark theme. Lock Now keeps the apps locked even with every habit done, and it can always be switched off.

Sheet 07 of 09/Blocker

The iPhone app

Blocker is 566 lines of Swift in 8 files, built from an XcodeGen spec into an app and a Monitor extension. Apple only gives out app tokens on the phone, so I pick the blocked apps there. Each change in the server's timeline becomes a one-off Screen Time schedule, up to 12, plus 4 daily checkpoints at 3 AM, 9 AM, 3 PM and 9 PM, to stay under the limit of about 20. When one fires, the Monitor asks the server first and falls back to the planned change if it's offline. It also sets a reminder 10 minutes before each complete by time, and Habits opens screenblock://sync after a save that changes the timeline.

Two of its rules came from bugs. Removing an app in the picker used to unlock it, so while locked the picker can only add. A stray backspace once wiped the saved key, so the key field now hides after a good sync.

An iPhone screen with a green open padlock, the word Unlocked and a blue Allow Screen Time button
PL 20The Blocker app built from its source and run in the iPhone simulator. Before Screen Time access it shows this one button.
An iPhone screen with a red padlock, the word Locked, the reason Water 3/4, a line reading Unlocks Saturday 4:00 AM, a status line reading 3/7 habits synced 8:10 PM, a background check line, and Pick apps and Sync now buttons
PL 21A demo build of Blocker in the simulator. This build skips Screen Time and the server and shows the state the demo habits give, locked by Water until 4:00 AM.
The same Blocker screen on a black background with the red padlock, Locked, Water 3/4 and the Pick apps and Sync now buttons
PL 22The same demo build in dark mode. The background check line says when the Monitor extension last asked the server, and the tick means it got an answer.

Sheet 08 of 09/Icons

Three icons for Blocker

There were three icon concepts, and I picked Check Shackle, where the check's long arm turns into the lock's shackle. The other two ship as alternate icons, and the App Icon page in Habits switches between them. Habits got its own icon twice on September 24.

An app icon on an indigo gradient with a white padlock whose shackle continues down into an indigo check mark on the lock body
PL 23Icon concept 1, Check Shackle, the one I picked. The long arm of the check runs up into the lock's shackle. This is logo.svg rendered at 1024 px.
An app icon mockup on an indigo gradient with a round lavender padlock holding a white disc and an indigo check mark
PL 24Icon concept 2, Habit Ring, a design mockup kept as an alternate icon.
An app icon mockup on an indigo gradient with a two tone white shield and an indigo check mark
PL 25Icon concept 3, Soft Shield, the other alternate mockup. Habits can switch Blocker between all three.
An icon of an open indigo ring with a black check mark inside and an orange dot in the ring's gap
PL 26The first home screen icon for Habits, from the commit of September 24.
An icon of a three line checklist with two orange ticked boxes and one empty black box
PL 27The checklist icon that replaced it that evening.

Sheet 09 of 09/Next

Known gaps

Turning off Blocker's Screen Time access in iOS Settings clears every shield, and only a Screen Time passcode someone else holds guards against that. There's no block on deleting apps, because the iOS setting for it covers every app on the phone. The schedules don't follow a time zone change until the next sync, and a reminder can still fire for a habit done after the last good fetch.