DEV Community

MN IN
MN IN

Posted on

Building an iOS app blocker with Screen Time APIs

I recently built a small iPhone app called FocusLock.

The idea is simple: choose distracting apps, create a rule, and let the phone enforce the boundary when it is time to focus.

I built it because I kept running into the same problem with my own phone habits. I did not need a bigger productivity system. I needed fewer tiny negotiations with myself.

This post is mostly about the technical side: what it is like to build this kind of app on iOS, what Apple gives you through the Screen Time APIs, and the tradeoffs I ran into while building the first version.

The main frameworks

FocusLock is built around a few Apple frameworks:

  • FamilyControls
  • ManagedSettings
  • DeviceActivity
  • UserNotifications
  • SwiftUI

At a high level:

  • FamilyControls lets the user choose apps or app categories through Apple's system picker.
  • ManagedSettings lets the app apply shields to the selected apps/categories.
  • DeviceActivity lets the system wake an extension when a scheduled interval starts or ends.

The important detail is that the app never receives normal app identifiers in the way people might expect. The selections are represented as opaque tokens. That is good for privacy, but it also shapes the product design. You cannot build a traditional analytics dashboard showing exactly which apps were opened. The system intentionally keeps that information constrained.

For an app like FocusLock, that is a feature, not a limitation.

The app / extension split

The main app handles the normal product surface:

  • onboarding
  • rule creation
  • app selection
  • focus timer
  • daily quota state
  • stats
  • settings

The Device Activity Monitor extension handles system-level enforcement:

  • a scheduled interval starts
  • the extension loads the stored app selection
  • it applies the shield using ManagedSettingsStore
  • when the interval ends, it clears the shield

That split is important because the main app may not be running. If the app is killed, a simple timer inside the app is not enough. Anything that has to happen reliably later needs to be represented in a way the system can execute.

That was especially important for daily quotas.

Schedule mode

The simplest rule type is schedule mode.

Example:

  • Monday to Friday
  • 9:00 AM to 6:00 PM
  • lock selected social apps

The app stores the rule locally, stores the FamilyActivitySelection in an App Group so the extension can read it, then registers a DeviceActivitySchedule.

When the interval starts, the extension applies the shield. When the interval ends, the extension clears it.

This is the cleanest model because the system owns the timing.

Daily quota mode

Daily quotas were trickier.

I did not want the app to be only a hard blocker. Sometimes a full block is too rigid. A quota feels more realistic: you can still use an app intentionally, but you have to decide whether it is worth spending part of the allowance.

The first version of quota mode works like this:

  1. The selected apps are normally shielded.
  2. The user taps "Use Now".
  3. The app clears the shield temporarily.
  4. It stores an unlock-until timestamp.
  5. It schedules a relock event.
  6. When time expires, the apps are shielded again.

The key lesson was that an in-memory timer is not enough. If the user swipes the app away, the relock still needs to happen.

So the app also registers a short DeviceActivitySchedule for the relock moment. The extension sees that event and reapplies the shield even if the main app is not alive.

That is the kind of edge case that seems small until you build the feature. A quota that sometimes fails to relock is worse than no quota at all.

Strict mode

Strict mode is a product decision more than a technical one.

Most app blockers fail at the same point: the user disables the blocker when they want the distraction most.

FocusLock handles this by making active strict rules harder to edit, disable, or delete. There is still an emergency unlock, because I do not think software should trap people. But the emergency flow adds friction: wait, confirm, and make the decision explicit.

That friction is the feature.

I tried to avoid making it feel moralistic. The goal is not to shame the user. The goal is to give the version of you that created the rule a little more weight when the current version of you wants to break it.

Privacy constraints

I wanted the app to be local-first.

FocusLock does not use analytics SDKs, ads, tracking, or accounts. Rules and preferences are stored on device. iCloud Key-Value Storage is used so settings can survive reinstalling the app.

The only network request in the app is optional: the focus timer can fetch Bing wallpapers for its background.

This also made the implementation simpler. There is no backend to secure, no account system to maintain, and no analytics event taxonomy to argue about. For this category, less infrastructure felt like the right choice.

What I learned

A few things stood out while building this:

1. Reliability matters more than feature count

If a lock does not apply at the right time, users lose trust immediately. I spent more time thinking about app lifecycle, relock behavior, and extension state than about adding more UI.

2. Opaque tokens force better privacy

Apple's model prevents a lot of tempting product ideas. You cannot casually turn app usage into a detailed behavioral profile. That can feel restrictive, but for a focus app, it aligns with the product's values.

3. Friction can be humane

Not every feature should optimize for the fewest taps. Sometimes the whole point is to slow the user down just enough to make a conscious choice.

4. The best productivity tool is often a boundary

I have tried many planners, trackers, and task systems. They can help, but the biggest improvement for me came from removing access to distractions before I needed willpower.

Current state

The first version includes:

  • scheduled app locks
  • daily quotas
  • progressive quota reduction
  • strict mode
  • emergency unlock
  • Pomodoro-style focus timer
  • achievements
  • daily and weekly reports
  • 14 languages
  • no account, no ads, no tracking

It is available on the App Store here:

https://apps.apple.com/us/app/focuslock-smart-app-lock/id6760992215

I am still learning what makes this kind of tool useful long term. If you have built with the Screen Time APIs, or if you have strong opinions about app blockers, I would be interested in hearing what worked and what did not.

The hard part is not blocking an app.

The hard part is designing a boundary people will actually respect when they are tired, bored, or avoiding something difficult.

Top comments (0)