## Inspiration
Earned came from a pattern we kept seeing in ourselves and the people around us. Someone notices that they are spending too much time on their phone, opens Screen Time, feels slightly horrified by the number, and decides to fix it. They set a limit on Instagram, YouTube, TikTok, or whichever app has been taking up the most time.
For a day or two, the limit feels useful. Then the warning appears at the exact moment they want a break. They tap “Ignore Limit.” Soon that becomes automatic. The limit still exists, but it has stopped meaning anything.
The next step is usually a stricter focus app. It adds countdowns, streaks, locked sessions, warning screens, and increasingly serious language. This can work for a while, but the relationship is still based on punishment. Every time the user wants to relax, the app treats that impulse as a failure of discipline. If they break the block, they lose a streak or feel as if they cheated. If they complete it, the only reward is that the punishment has ended.
We found that strange. People do not usually struggle with focus because they do not know distractions exist. They struggle because the immediate reward of opening an entertaining app is stronger than the distant reward of finishing an essay, revising for an exam, or completing a difficult piece of work.
Most blockers try to make the distraction more painful. We wanted to make progress more rewarding.
The idea became much clearer when we stopped treating leisure as the enemy. Watching a video, messaging a friend, or checking social media is not automatically bad. The problem is losing control over when it happens and how long it lasts. We wanted to create a healthier bargain where focused work and intentional leisure could support each other.
That led to the central question behind Earned:
**What if access to the apps you enjoy was something you earned by completing work you genuinely cared about?**
The inspiration was not to build a stronger lock. It was to build a reason to stay focused.
We also wanted to address something timers usually ignore. Sitting at a desk for an hour does not always mean an hour of meaningful progress. A timer can measure absence from distraction, but it cannot prove that the intended work happened. We were interested in the difference between serving time and finishing something.
That distinction matters especially for students. You can spend forty-five minutes with a textbook open and still avoid the chapter you were supposed to revise. You can keep a document on screen without writing the paragraph. You can complete a focus timer and still have nothing concrete to show for it.
We wanted the finish line to be based on an outcome chosen before the work began. The user should decide what “done” means while they are still thinking clearly, not rewrite the standard afterward because the timer reached zero.
The reward also had to feel real. Points and badges can be satisfying, but they are easy to ignore when the thing you actually want is already sitting on the same phone. Time itself felt more honest. Focused work creates a limited amount of intentional leisure, and that leisure can be enjoyed without guilt because it has a clear boundary.
Earned grew from that shift in perspective. Instead of asking, “How can we stop people from using their phones?” we asked, “How can a phone make progress feel worth choosing?”
Punishment can interrupt a habit, but it rarely creates lasting motivation. A reward gives the effort somewhere to go.
## What it does
Earned is a native iOS focus and reward app built around one complete cycle: define the work, protect the time, provide evidence, and spend the access that was earned.
During onboarding, the user grants Screen Time authorization and privately selects the apps, categories, and websites that usually interrupt their work. Earned uses Apple’s Family Controls system, which represents those selections through private tokens. The app does not need to know or record the names of the distractions the user selected.
The user then creates a focus contract. Instead of entering only a timer duration, they describe a concrete goal, optional completion criteria, and the first step they plan to take. They can begin immediately or schedule the contract for later.
Before the contract begins, Earned shows the potential reward. The user chooses a reward ratio that determines how many focused minutes are required to earn one access minute. The calculation is visible before the session starts, so there is no hidden score or unclear conversion system.
Once the contract begins, Earned activates Screen Time shields over the selected distractions. The focus view displays the promised outcome, a shrinking countdown ring, the remaining time, and any check-ins recorded during the session. The contract continues even if the main app is closed because the deadline is stored as an absolute date rather than maintained through an in-memory timer.
A Live Activity keeps the contract visible from the Lock Screen and Dynamic Island. Home Screen widgets show the active session, banked access minutes, focused time, and streak information. The user can check the remaining time without repeatedly opening Earned and pulling themselves away from the task.
When the focus period ends, the contract does not immediately produce a reward. It moves into proof review.
The user can describe what they completed in writing, dictate their result using on-device speech recognition, complete a checklist, attach a file or image, disclose a link, answer a follow-up question, or use a short live proof check in supported development builds. The proof remains editable until it is submitted.
The review system compares the evidence with the original goal and completion criteria. It returns a structured decision containing an approval status, a confidence value, a concise reason, a list of missing evidence, and a focused follow-up question when one is needed.
The current release-safe reviewer runs locally. It can inspect written evidence and attachment metadata while clearly admitting that it cannot inspect the contents of an ordinary attachment or independently verify a linked claim. Development builds can connect to a structured remote review provider after the user gives explicit upload consent.
If the first submission does not contain enough evidence, the user receives one resubmission. This gives them a fair chance to clarify the result without allowing the review to become an endless negotiation. If the reviewer is still wrong, the user can record a manual override with a written reason. The override remains visible in the contract history.
Once a contract is approved, Earned calculates the reward and adds it to an append-only minute ledger. Each reward has a unique idempotency key, which means the same focus contract cannot accidentally grant its reward twice.
The user can spend their balance by creating an Access Pass for selected apps. Earned temporarily removes the relevant Screen Time shields and starts monitoring the selected foreground usage. The pass ends when the purchased usage has been consumed or when the claim window expires, whichever happens first. The apps are then protected again.
Only one Access Pass can be active at a time. The requested minutes are committed when the pass begins, preventing two actions from spending the same balance.
Earned also includes a ten-minute emergency reserve. A focus system should not make urgent communication impossible because the user has no banked time. The reserve is visible and recorded, which keeps it available for genuine needs without turning it into a hidden bypass.
The History section records completed, approved, overridden, abandoned, interrupted, and rewarded contracts. It also preserves review reasoning, check-ins, reward grants, and Access Pass redemptions. This gives the user a record of actual outcomes rather than another dashboard filled only with screen-time totals.
## How we built it
Earned is written in Swift 6 using SwiftUI for the interface and SwiftData for persistent storage. The project is generated from a checked-in XcodeGen configuration so bundle identifiers, capabilities, targets, and build settings remain consistent and reviewable.
The project contains five cooperating targets. The main iOS app manages the product flow and persistent records. A Device Activity Monitor extension receives system callbacks. A Shield Configuration extension controls the appearance of blocked screens. A Shield Action extension handles actions taken from those screens. A WidgetKit extension provides Home Screen widgets, Live Activities, and Dynamic Island layouts.
Apple’s Screen Time frameworks form the enforcement layer. FamilyControls handles individual authorization and private app selection. ManagedSettings applies the shields. DeviceActivity schedules focus periods and tracks reward usage. ManagedSettingsUI supplies the system-controlled blocked-app interface.
These frameworks do not behave like a normal timer library. The main app may be suspended or terminated when a session ends. Extensions can receive callbacks without the main process running. Some callbacks can also arrive later than expected.
To manage this, we created a small, versioned `EnforcementSnapshot` shared through an App Group. The snapshot contains only the information the extensions need, including the current mode, session identifier, deadlines, event identifiers, and encoded private selections.
Snapshot writes are atomic. If a write is interrupted, Earned selects the newest valid copy. Cross-process mutations use a lock so the app and its extensions cannot overwrite each other at the same time.
Each extension event is checked against the current session or reward identifier before it can change the shields. This prevents a delayed callback from an older session from ending a newer contract.
SwiftData stores focus contracts, review decisions, application settings, reward ledger entries, and Access Pass records. Earned treats persisted timestamps as the source of truth. The visible countdown is only a presentation of those dates.
When the app launches or returns to the foreground, it runs a reconciliation process. This compares SwiftData, Screen Time authorization, Device Activity monitoring, the shared enforcement snapshot, active rewards, widgets, and Live Activities. If something is out of sync, Earned repairs what it safely can and reports the rest.
ActivityKit powers the Live Activity. WidgetKit presents the same state on the Home Screen, Lock Screen, and Dynamic Island. Deep links return the user directly to the related contract or Access Pass.
The review flow is abstracted behind an `AIProvider` protocol. This allows the user experience and decision model to remain independent from a specific provider. Every implementation must return the same structured review object.
Malformed responses are rejected without consuming a review attempt. Release builds use the deterministic local provider. Experimental remote providers are contained behind debug compilation flags and require explicit consent before any proof is uploaded.
Voice proof uses Apple’s Speech framework with on-device recognition. AVFoundation manages microphone permission, audio routes, the recording session, and the input tap. If private on-device recognition is unavailable, the interface tells the user to type their proof instead.
The optional live proof check uses AVFoundation camera capture. It is started only when the user requests it. The system captures no more than three compressed frames, keeps them in memory, and clears them when the review draft is deleted or the screen is dismissed. It is sampled proof rather than continuous recording.
The reward system was implemented as a ledger rather than a single mutable balance. Grants, redemptions, and expirations are separate entries. The current balance is derived from those entries and clamped at zero.
A reward is recorded only once. An Access Pass cannot begin without enough balance and a selected app. Only one pass may be active, and a redemption is saved only after Screen Time monitoring starts successfully. If setup fails, Earned restores the previous shield state instead of charging the user for access that never opened.
We extracted deterministic reward and session rules into a small testable core. The tests cover reward calculations, invalid ratios, negative balances, overspending, duplicate grants, simultaneous passes, minimum durations, emergency reserve usage, and the one-resubmission policy.
We used AI-assisted development to accelerate research, explore unfamiliar Apple frameworks, compare architectural approaches, and generate failure cases worth testing. It helped us move faster, but it did not replace physical-device testing or product judgment. A generated solution could compile while still being wrong about extension timing, app lifecycle, entitlements, actor isolation, persistence, or privacy.
The visual design was based on a ruled work ticket rather than the usual focus-app aesthetic. The interface uses cool paper surfaces, dark carbon text, fluorescent earned-time marks, cobalt approval stamps, red exception states, and monospaced timer digits. We wanted each contract to feel like a specific agreement rather than another vague productivity score.
## Challenges we ran into
The hardest part of Earned was making several independent parts of iOS agree about the same focus contract.
Screen Time enforcement is distributed across the app and multiple extensions. The main app can believe a timer has ended while the operating system has not yet delivered the monitor callback. A reward event from an older Access Pass can arrive after a new focus contract has started. Authorization can also be revoked from outside Earned at any time.
Solving this required more than starting and stopping a timer. We needed session identifiers, absolute deadlines, atomic App Group snapshots, a fixed named Managed Settings store, cross-process locking, and a reconciliation system that runs whenever the app becomes active.
Apple’s Device Activity timing rules created another problem. A short Access Pass may last only a few minutes, but the underlying activity schedule still needs to satisfy Apple’s minimum interval. We could not simply schedule a five-minute window and assume iOS would deliver a callback at exactly five minutes.
We handled this by combining a valid platform schedule with a foreground-usage threshold and a separate claim deadline. The selected apps are protected again when the purchased usage is consumed or when the claim window expires.
The private Screen Time tokens also changed how we approached development. Earned cannot decode them into convenient bundle identifiers or app names. That made debugging and analytics less straightforward, but respecting that limitation was important. The app works with private selections and counts without constructing a record of the user’s distractions.
SwiftData caused one of the most frustrating crashes in the project. A physical-device build could launch, stay open for several seconds, and then terminate because the local model container could not open its store.
That type of crash is difficult because it happens before the normal interface can recover. A broken screen can display an error. A failed model container can prevent the entire application from presenting anything.
The failure forced us to think carefully about schema changes, old development stores, corrupted data, initialization order, and recovery. Deleting the database would have been an easy development fix, but it would be unacceptable in a real release because the store contains reward history and completed contracts.
We learned that persistence recovery needs to distinguish disposable test data from user records that require migration or export. This is now one of the highest-priority areas before public release.
Audio introduced a different set of crashes. Microphone permission callbacks can arrive on private queues. Swift 6 actor isolation makes incorrect assumptions about those callbacks visible. The microphone route may still be starting when the app requests its input format, and an old audio engine can retain a stale node or input tap.
We eventually treated each recording attempt as a fresh generation. Starting, cancelling, dismissing the screen, changing routes, or responding to permissions invalidates earlier callbacks. The capture service creates a fresh engine, validates the input format, installs one tap, and tears down the audio session when recording ends.
This was one of the areas that would have been especially difficult to implement entirely alone from scratch. AVFoundation, Speech, Swift concurrency, and SwiftUI lifecycle behavior all intersect in a small section of the interface. A mistake in any one of them can create a crash that appears unrelated to the code that caused it.
File size became another serious challenge during local speech experimentation. One full-precision model asset was over 300 MB, and another packaged variant was roughly 156 MB. Either model would have dominated the application bundle before counting the app, extensions, images, fonts, and other resources.
That forced us to separate experimental assets from shipping resources and explicitly exclude the local model directory from the main application target. The current voice proof path uses Apple’s supported on-device recognition instead.
A future custom model would need quantization, optional downloading, integrity verification, compatibility checks, cleanup controls, and a clear quality advantage before it justified that size. Building those systems correctly would be a substantial project by itself.
Signing and entitlements were also much harder than they appear from the finished interface. Earned is not one application bundle. The host app and Screen Time extensions need consistent App Group and Family Controls capabilities. Provisioning mistakes can allow one target to compile while another fails to embed, sign, install, or communicate.
Development signing is also different from public distribution. Every shipping bundle that uses Family Controls needs Apple’s distribution approval. A successful local build does not mean the same archive is ready for the App Store.
We also learned that installing an app on an iPhone is not the same as testing it. A build can compile, sign, embed every extension, and install correctly while still failing to launch because the phone is locked. Runtime verification required an unlocked device and visible inspection of onboarding, focus enforcement, review, rewards, widgets, backgrounding, and relaunch.
The review system created a product challenge as much as a coding challenge. An automated reviewer can produce a confident answer even when it has misunderstood the evidence. We did not want Earned to claim that attachment metadata proved the attachment’s contents.
We added explicit uncertainty, focused follow-up questions, one resubmission, and a recorded manual override. The system can assist with judgment without pretending to be infallible.
Reward correctness was deceptively technical. A simple stored integer could be changed twice by duplicate callbacks, repeated button taps, interrupted saves, or overlapping Access Pass requests. The append-only ledger and idempotency rules required more code, but they made every reward minute traceable.
AI-assisted coding helped with the scale of these problems. It could quickly compare framework documentation, identify edge cases, and generate possible implementations. It was especially useful when a feature crossed several APIs at once.
The difficult part was knowing when the generated answer was incomplete. Code could look polished and compile successfully while ignoring a delayed extension callback, an unavailable audio route, a locked device, a huge bundled model, an old SwiftData store, or an entitlement that only fails during distribution.
The project taught us that development speed and development confidence are different things. AI increased the first. Testing, debugging, and careful architecture were still responsible for the second.
## Accomplishments that we're proud of
We are proud that Earned became a complete working loop rather than a collection of mock screens. A user can define a contract, start Screen Time protection, follow the countdown, submit evidence, receive a structured review, earn access minutes, and spend those minutes through a bounded Access Pass.
We built the product around outcomes rather than elapsed time. Completing a timer does not automatically create a reward. The user has to show what changed, and the review remains tied to the goal they wrote before the session began.
The Screen Time layer respects Apple’s privacy model. App selections remain opaque. Earned does not extract app identities, send private tokens to a server, or perform network work inside the shield extensions.
We are proud of the shared enforcement architecture. Atomic snapshots, cross-process locking, versioned state, event identifiers, and deadline checks are not highly visible features, but they prevent old callbacks and interrupted writes from changing the wrong contract.
The reward ledger is another accomplishment that mostly stays behind the interface. Duplicate events cannot mint the same reward twice. Two Access Passes cannot spend the same balance. Failed monitoring does not charge the user for access that never opened.
We are also proud of the review flow. It does not reduce completion to the presence of a photo or a block of generated encouragement. The decision is connected to the original criteria. Missing evidence is specific. The user receives a focused chance to clarify and retains the final ability to override an incorrect decision.
The Live Activity and Dynamic Island experience made Earned feel native to iOS. The user can see the remaining contract time without reopening the app. The same underlying state is reflected across the active focus screen, Lock Screen, Dynamic Island, widgets, history, and Access Pass.
We gave real attention to failure states. Permission loss, persistence failure, unavailable microphone input, invalid review output, failed Screen Time monitoring, duplicate rewards, and interrupted enforcement all have different consequences. Earned does not hide those differences behind one generic error.
We are proud of the visual identity. Earned does not resemble a meditation app, a streak game, or a generic blocker. It looks like a record of an agreement. The fluorescent reward marks give the interface energy while the rest of the design stays calm and readable.
Most importantly, Earned has a clear point of view. Focus time matters when it produces something observable. Leisure is valuable when it is intentional. The app connects those ideas without framing every distraction as a moral failure.
## What we learned
We learned that iOS extensions completely change how application state needs to be designed. The main app cannot be the only authority because it may not be running when a schedule ends or a usage threshold is reached.
Shared extension state needs to be small, versioned, atomic, and understandable without loading the main database. Events also need enough identity to prove that they still belong to the active contract.
We learned to trust dates more than timers. A countdown displayed on screen is presentation. Persisted start and end dates determine what actually happened. This made recovery after suspension, termination, and delayed callbacks much more reliable.
We learned that privacy constraints can improve a design. Opaque app tokens initially felt restrictive, but they prevented us from collecting information Earned did not need. The app can protect a distraction without building a detailed inventory of it.
We learned that review systems should expose uncertainty. Confidence should not be hidden behind a binary result. Evidence gaps should be understandable, and the user needs a route to disagree without silently deleting the history of that disagreement.
We learned that a reward balance behaves like a financial system in miniature. Once minutes can be earned and spent, duplicate grants, overspending, failed redemptions, expiration, and concurrent actions all matter.
We learned that local-first does not automatically mean simple. Local persistence, app lifecycle changes, background execution, extension state, and schema evolution can create serious reliability problems even without a traditional backend.
We learned to inspect every large dependency and resource before allowing it into the application target. A local model may make an experiment feel self-contained, but hundreds of megabytes affect download size, installation time, build speed, memory pressure, and update delivery.
We learned that physical-device testing changes the project. The simulator is useful for layout and navigation, but it cannot prove Screen Time shields, Device Activity thresholds, camera capture, microphone routes, signing, or extension delivery.
We also learned where AI-assisted development helps and where it stops helping. It is excellent for exploring an unfamiliar framework, producing alternatives, and identifying edge cases worth testing.
It cannot unlock the phone, approve an entitlement, inspect a delayed callback, recover a corrupted database, or decide whether deleting a user’s reward history is acceptable. Those decisions still require human judgment.
## What's next for Earned
The immediate priority is persistence reliability. Earned needs a proper SwiftData migration plan, a recovery interface for stores that cannot open, and an export path before any repair operation is allowed to remove user history.
We also need to complete a wider physical-device test matrix. This includes termination during focus, relaunch after the deadline, reboot, permission revocation, usage-threshold exhaustion, time-zone changes, midnight boundaries, locked-device behavior, and delayed extension callbacks.
The Family Controls distribution entitlement must be approved for every shipping target before public release. Development signing proves that the architecture can run on our devices, but it does not replace Apple’s distribution process.
The production review system will move behind a product-owned server using a documented structured API. Requests will be authenticated, bounded, and sent only after explicit consent. API credentials will remain server-side.
That system will also need attachment redaction, retention controls, deletion APIs, abuse protection, rate limits, and a consent receipt showing what evidence was uploaded.
We want to improve proof without turning Earned into surveillance. Better document extraction, optional before-and-after comparisons, and criterion-specific questions could make review more useful. Continuous camera monitoring, background audio recording, and hidden uploads do not fit the product we want to build.
Recurring contracts are planned, but only after one-off contracts are dependable. Repeating an unreliable schedule would multiply confusion. We first want to understand whether users complete the review and use their earned time as intended.
Reward policy will gain more user control. The current Access Pass commits the selected minutes when it begins. Future versions could let users choose whether unused minutes are returned, forfeited, or partially refunded. The policy must always be visible before the pass starts.
History will gain export and delete-all controls. We also want to provide better summaries of verified focus, completed outcomes, reward usage, and long-term patterns without identifying the protected apps themselves.
A custom on-device review model remains possible, but only if it earns its place in the bundle. We would first test compressed models, optional downloads, device compatibility, integrity verification, and storage cleanup. The default installation should remain small and dependable.
Longer term, Earned could support different contract types for studying, writing, coding, exercise, and administrative work. Each type could use more relevant completion criteria while keeping the same underlying agreement.
The promise of Earned will remain simple: decide what progress means before the timer begins, show what changed afterward, and spend the reward deliberately.
Log in or sign up for Devpost to join the conversation.