All work

Native iOS app · SwiftUI · Designed & built solo

I had never opened Xcode. A week later, I was using the app I built.

Project
Second Mind
Role
Design & iOS engineering, solo
Platform
iOS 17+ · SwiftUI · SwiftData
Timeline
2026 · Ongoing personal project

1 sentence

The problem was capture. Say one line and it becomes a real reminder. No forms, no fields

3 triggers

It fires on time, on arriving at a saved place, or on an Apple Health signal. Not just alarms

Every morning

The honest measure for an app built for one user: it's still the first thing I open

I designed it, then I shipped it

Second Mind is a native iOS app written in Swift: a habit, routine, and reminder companion I designed and built for exactly one user: me. It runs on my phone, on-device, with Apple Health and location wired in.

The starting point was genuinely zero. I asked what an iPhone app is even written in, the answer was Swift, and I installed Xcode that day. Inside a week the first version was on my phone and I was using it. What you see below is the third version. The first two are further down the page, unretouched.

It runs on my phone, not in a mockup

The Second Mind widget on an iOS home screen, showing the next habit, a 33-day streak and the rest of today's list

That’s a real iOS home screen. The widget is reading live data straight out of the app’s own store: next habit, current streak, how much of today is left.

SwiftUI
Every screen written as code. No template, no page builder.
SwiftData
Nine on-device models: habits, routines, reminders, memories.
HealthKit
Steps, sleep and workouts read as triggers.
CoreLocation · MapKit
Geofenced places, so a routine can fire when I get home.
WidgetKit
Three home-screen widgets, fed by the app's own store.
Cloudflare Worker
Ask Mind's backend: a sentence in, typed JSON out.

178 Swift files across 61 commits, every one of them mine to review.

I used AI as an implementation partner while writing the Swift, but the product decisions, the design system, and the judgment calls below are mine, and I reviewed every change that went in.

The problem

My problem was never motivation. It was capture. The small, recurring things (a supplement, a birthday, a stretch) died between a notes app, a reminders app, and a habit tracker before I ever wrote them down. And the few reminders I did set were blind to context: they fired on a clock, never when I actually arrived somewhere or finished a workout.

I didn't need another productivity app. I needed a second mind: one that captures a thought in a sentence, remembers it, and brings it back at the exact moment it matters.

How my judgment changed

The interesting part of this project isn't the final screens. It's the distance between them. I rebuilt the whole app three times. Same person, same tools; what changed was judgment about hierarchy, visual weight, and when the app should act on its own.

Three designs, one app

Same person, same tools. Each screen captured from the Simulator at that version’s own commit.

01

MVP

First build

Stock SwiftUI, default components, empty states. The AI tab was still a “coming soon” placeholder.

MVP: Home
Home
MVP: Reminders
Reminders
MVP: Chat, “coming soon”
Chat, “coming soon”
02

Routine Pro (dark)

First rebuild

First real design system, but a heavy, dashboard-led dark theme that competed with the content for attention.

Routine Pro (dark): Home
Home
Routine Pro (dark): Routine
Routine
Routine Pro (dark): Ask Mind
Ask Mind
03

Current

Shipping today

A calmer light system built on tokens. Clear morning hierarchy, health context in view, and AI folded into existing routines.

Current: Home
Home
Current: Routine
Routine
Current: Ask Mind
Ask Mind

The MVP leaned on defaults and empty states. The dark rebuild gave me a real system but fought the content for attention. The current version is quieter on purpose: a clear morning hierarchy, health context in view, and the AI folded into routines I already had instead of a separate "chat" tab.

The decision that shaped the app

Every capture flow lives or dies on friction. The core call was how Ask Mind should behave when I type or say something like "remind me to take vitamins at 8 AM."

The decision

Ask Mind: commit silently, or propose and let me accept?

Problem
Capture dies the moment it costs effort, but a misread sentence that silently becomes a daily 8 AM alarm is worse than not capturing at all.
Rejected
Open a form. Parse nothing, just show the create screen with the fields empty. That is the friction that kills capture in the first place.
Also rejected
Commit silently. Create the reminder and move on. Fastest possible capture, and every wrong guess turns into a real alarm I have to hunt down.
Chosen
Parse into a finished proposal. Every field is already filled (repeat, date, time) and one tap accepts it. Corrections arrive as suggestions, never as empty fields.
Trade-off
One extra tap on every capture, in exchange for never cleaning up after the model.
One sentence in, a filled-in proposal I accept with one tap, and a reminder that can fire on a time, at a place, or on an Apple Health signal
One sentence in, a filled-in proposal I accept with one tap, and a reminder that can fire on a time, at a place, or on an Apple Health signal
One sentence in, a filled-in proposal I accept with one tap, and a reminder that can fire on a time, at a place, or on an Apple Health signal
One sentence in, a filled-in proposal I accept with one tap, and a reminder that can fire on a time, at a place, or on an Apple Health signal
Ask Mind reading a Vitamin D3 bottle, dating the run-out, and offering to add it to a morning routine

Ask Mind

Our AI intelligence

A photo of a bottle becomes a labelled product, a run-out date, and a routine. Typed, spoken, or snapped. The model proposes. You accept.

  1. 01

    It sees the bottle

    Gemini vision reads the label: Vitamin D3, 2000 IU, 90 capsules.

  2. 02

    It asks one question

    One follow-up. Once daily is enough to finish the picture.

  3. 03

    It does the counting

    90 capsules, one a day, run out around November 16th.

  4. 04

    It files it away

    Add to Morning routine. The reminder lives on-device in SwiftData.

SwiftUI · SwiftData

The reminder stays on the phone.

Cloudflare Worker

One turn, streamed from the edge.

Gemini

Vision plus a fixed JSON schema.

Cloudinary · Firestore

Photo stored. The app keeps the link.

Only a one-line health summary and place names leave the device.

One vocabulary, every screen

Because I was designing and shipping alone, drift was the real risk. So the app runs on one token vocabulary, named the same way in Figma and in code: primitives hold the raw palette, semantic tokens describe intent, and screens are only allowed to use the semantic layer. New screens can't quietly diverge from old ones.

Second-mind-design-system / Components

Primitives

amber

50

100

200

300

400

500

600

700

800

900

Input / Focused

Remind me at 8 AM

Button / Primary · Secondary

Save reminder
Cancel

MCP

App/Theme/DesignSystem.swift
enum Primitive {
    static let neutral50     = Color(hex: "#F8F7F4")
    static let neutral900    = Color(hex: "#1C1B18")
    static let accentDefault = Color(hex: "#F2B84B")
    static let streakFlame   = Color(hex: "#FF7A1A")
}

enum AppColors {
    static let background         = Primitive.neutral50
    static let buttonPrimaryBg    = Primitive.neutral950
    static let buttonPrimaryLabel = Primitive.neutral0
    static let borderActive       = Primitive.accentDefault
}

Same names in Figma and in Swift. Switch the tab to see the tokens, then the button and input that consume them.

Home, Routines, and Analytics: one warm surface, one set of components, one source of truth for every number
Home, Routines, and Analytics: one warm surface, one set of components, one source of truth for every number
Home, Routines, and Analytics: one warm surface, one set of components, one source of truth for every number
Home, Routines, and Analytics: one warm surface, one set of components, one source of truth for every number

The honest outcome

This was built for one user, so I won't dress it up as market validation. The evidence I can actually stand behind is simpler: it's still the first thing I open every morning, and it survived being rebuilt three times because each version was easier to live with than the last.