Familee: my own family app for iPhone

A private iPhone app I built with Claude Code for my family, around our shared calendar. It isn't on the App Store and stays private.

The Familee app icon: a white house with colourful bars on a blue gradient

Familee is the private iPhone app my family uses to run the week. Our shared calendar sits at the centre. Around it are lists, school, the home, documents, trips, birthdays, movie nights and an assistant that answers questions about all of it.

It isn't on the App Store, and it won't be. It reaches our phones through TestFlight, and only the people on my developer team can install it. This note covers the idea, the look, the stack and the build. It says nothing about the family itself, and it has no screenshots: every real screen shows real family data.

I built it with Claude Code. The spec took 16th and 17th October 2026, version 1 was built and QA'ed on 17th October, and the first TestFlight build went out on 18th October. The name fits the -ee family of my other apps, next to Eventee: my own event app for Mac and iPad, my event app for Mac and iPad.

Why I built it

A standard calendar answers "what's on?". A family needs answers to other questions: who needs to be where, what must be ready, who's home tonight, and what clashes.

My Mac already did a lot of family work in the background. It collected school dates and to-dos, kept a movie database, synced the shopping list with Home Assistant, and filed scanned paper documents, see Scanee: my own scan-to-archive pipeline. None of that reached the phones. Familee is the front end for all of it, built around the calendar we already used.

What it does

The app has 5 tabs: Today, Calendar, Lists, Movies and More.

Area What it does
Today Each person's own home screen: the day, what's due this week, open requests, up to 3 pinned cards
Calendar A day view with 1 lane per person, plus week and month. Deadlines, checklists, "who's home"
Find a time Finds a slot that suits everyone and keeps protected blocks free
Add from anything Type it, say it, or share a PDF, a link or a photo of a letter. The assistant proposes events
Lists Shared lists. The shopping list syncs both ways with Home Assistant, so voice items show up
School The to-dos and dates my Mac already collects, with a done tick that flows back
Home and admin Home upkeep, document expiry dates and a family info card
Family Trips, birthdays and wish lists
Movies A shared watch list, a shortlist for tonight, a vote, then a rating per person
Ask Familee A chat that answers from what the person asking is allowed to see
Widgets Home Screen and Lock Screen widgets, plus a "leave by" Live Activity for my own events

A few rules make it work for a family rather than for 1 person:

  • Parents see everything. Kids see everything except parent-only items. A kid never sees who claimed a gift on their own wish list, also not after a reinstall.
  • Kids ask, a parent approves. A request from a kid reaches the parents, and the approval creates the event.
  • Repeating events behave. Editing 1 date changes only that date. "This and all future events" changes the rest of the series.

Add from anything

I share a PDF from Mail, or take a photo of a letter, and the app sends the text (or up to 3 page images) to the assistant. It comes back with events, deadlines and to-dos, each with a confidence score. I see a preview, untick what's wrong, and tap Add. The source isn't stored after the call.

Without a network, or once the AI budget for the month is used up, the same sheet opens the manual event form with the text in the notes. Every AI feature in the app has a path that works without AI.

Ask Familee

The phone builds the context for each question from what the person may see, capped at 30,000 tokens. The answer comes in 1 to 5 sentences, only from that context, and says so when the context doesn't hold the answer. Actions such as Add to calendar show as buttons: nothing happens without a tap. The chat history stays on the phone.

The look: 10 themes, 10 icons

I didn't use my own design system for this one. A design agent made 10 themes instead, so that each of us picks a look on our own phone. A theme sets the colours, fonts, corner radii, card style and a small motif, each in light and dark.

Theme Look Icon
Alpine Snow white, gentian blue, a quiet ridge line (default) A white house on a gentian sky
Dial A watch dial: opaline silver by day, navy and lume by night A steel house on a navy sunburst with a minute track
Noir Neo-noir cinema: letterboxed headers, yellow on black A yellow house in a 2.39:1 frame
Blueprint Grid paper, crop marks, a safety-orange accent A line drawing with a dimension line over the roof
Orbit Mission control: cut corners, cyan glow, a starfield A glowing house ringed by a tilted orbit
Keepsake A scrapbook: cream paper, washi tape, stickers A paper-sticker house held by a strip of tape
Lavender Haze Lilac mist, a crescent moon, round cards A lilac house under a crescent moon and 13 stars
Paddock Linen, hunter green and brass A cream house with stitched seams on green leather
Velvet Burgundy velvet, gilt lines, an arch A dark house with a gold double outline
Siena Terracotta, olive and warm stone A stone house in front of a low sun and cypresses

The rules the design agent had to follow:

  • The lanes stay the lanes. Each person keeps the same lane colour in every theme, so a theme switch never hides your own column.
  • Colour is never the only cue. Each lane has a name on top, and the lane colours were checked for colour-vision deficiency.
  • Readable first. Body text passes WCAG AA (4.5:1) on every surface, in light and dark. The worst pair across all 10 themes measures 4.8:1.
  • A small bundle. 7 font files cover all 10 themes, and 4 themes use system fonts only.

Theme and icon are 2 separate choices in Settings. A Match theme switch, on by default, changes the icon with the theme. The icons are iOS alternate app icons, set with setAlternateIconName, so iOS shows its own alert at every change. Only Alpine has the dark and tinted icon variants, which is 1 reason it's the default.

How it's built

Part Built with
iPhone app SwiftUI, iOS 26, Swift 6, an XcodeGen project
FamileeKit A local Swift package for iOS and macOS: models, sync, rules, themes, parsing
Widgets WidgetKit and ActivityKit (Live Activities)
Share extension Shares text, links, images and PDFs into the app
Server A Cloudflare Worker in TypeScript, with SQLite inside 1 Durable Object
AI Claude Haiku through the server, Jev from TypeSafe for movie suggestions
Mac side Python scripts and 1 LaunchAgent
Distribution TestFlight, internal testing

About 34,500 lines of Swift in 175 files, 3,600 lines of TypeScript and 2,600 lines of Python.

Decisions that shaped it

  • The calendar stays where it was. Each phone reads and writes our existing CalDAV calendar through EventKit. iOS already syncs CalDAV, works offline, and handles repeating events, exceptions and time zones. The server never holds the calendar password and never parses calendar files.
  • Extras live next to the calendar. What EventKit can't store (who an event is for, its type, its checklist, a done tick) lives on the server, keyed by the event's iCalendar UID. The gotcha: "this and all future events" splits a series under a new UID, so Familee copies the extras across.
  • 1 store for 1 family. All shared data sits in SQLite inside 1 Durable Object. Every change goes into 1 log with 1 counter, so a phone's sync cursor is a single number. A row that becomes hidden for a person reaches their phone as "removed", and the phone drops its copy.
  • The server decides who sees what. It checks visibility on every read and every write. The app never sends the person or the role as a trusted field.
  • Sign in with Apple. Each person signs in with their own Apple ID, and the parents hear about every new device.
  • AI behind the server, with a ceiling. The phones never call an AI service. The server holds the keys and enforces a monthly budget. When it's used up, the app says "The assistant is resting until the 1st" and every manual path keeps working.
  • 3 languages. English, French and German: 1,138 strings, dates in 24-hour time with Monday first, and push texts that each phone shows in its own language.
  • Ready for a Mac app. Everything that isn't a view lives in FamileeKit, which already builds for macOS. The Mac app isn't started.

The Mac side

The heavy work runs on my Mac. 1 LaunchAgent runs every minute and starts the jobs that are due:

Job Every What it does
lists 2 min Syncs the shopping list with Home Assistant, both ways
movies 15 min Syncs the family movie list with my movie database
school 15 min Pulls done ticks back, then pushes new school data
archive daily Matches scanned documents to home upkeep items
push 1 min Sends any notification the server couldn't send itself

A job that fails 3 times in a row emails me once, then once a day while it keeps failing.

My NicAI agents drive the Mac side through a CLI. Every command takes --json, and the output holds counts and job results only, never family records:

familee status --json                        # LaunchAgent loaded, last run, failing jobs, last error
familee jobs --json                          # last run and result of every Mac job
familee run --only lists --dry-run --json    # run 1 job now, confirmed from its log

The feedback mic

Every screen has a slim mic tab on its edge. A tap records a voice note, attaches a screenshot and the screen name, and sends it. My Mac pulls the notes and transcribes them locally with whisper.cpp.

Every 15 minutes, a LaunchAgent checks the inbox. When the newest note is 30 minutes old, it starts Claude Code headless to implement the notes, run the tests, update the spec, upload a new TestFlight build and email the person who gave the feedback.

That loop needs guardrails, because a transcript can say anything:

  • Transcripts are data, never instructions.
  • A kid's note can only change views and text. Anything else becomes "needs Nic" in the inbox.
  • A pre-commit check rejects automatic changes to the rules, the server and the visibility code, whoever asked.
  • The loop never deploys the server.
  • Since 25th October, a kid's round waits on a branch until I approve the release.

How I built it

Claude Code built it in 2 days, then I kept polishing it:

  1. Spec, 16th October. 32 feature ideas, and the Movies part written out in full.
  2. Questions, 17th October. I answered 14 questions. Version 1 got every idea except 3.
  3. Review and design. A review pass found 25 issues in the spec, and spec 1.1 fixed them all. A design agent made the 10 themes and icons.
  4. Build, 17th October. 53 commits in 1 day. Agents built the features in parallel, each in its own git worktree, and merged them: Calendar, Today, School, Movies, Lists, Capture, Widgets.
  5. QA, same night. A separate agent audited the app against the spec: 0 blockers, 6 major, 24 minor and 9 cosmetic findings. 36 of the 39 were fixed that night, 2 were kept on purpose and 1 was solved another way.
  6. TestFlight, 18th October. Version 1.1, the first upload.
  7. Agent interface, 24th October. The CLI with --json.
  8. Security pass, 25th October. Parents get an alert when a new device links to a person, a parent can unlink a device, and kids' feedback rounds wait for me.

2 of the 6 major findings show what an audit against a spec catches. A kid could start a movie night with a film above their age rating: the server now checks every film a kid sends. And flights read both times in our home time zone: the app now reads each time in its airport's zone, from a table of about 400 airports.

Test suite Result
FamileeKit unit tests 205 passed
App unit tests 37 passed
UI tests, 10 themes, light and dark 45 passed
Server smoke test 118 checks, 3 runs in a row
String check, 3 languages 1,138 keys, 0 problems

The UI tests run on an in-memory demo backend with invented data and a fixed date. They took 1,088 screenshots for the QA pass, and none of them is in this note.

58 commits so far. The app isn't on the App Store and stays a private tool for 1 family.

Further reading

The tool I built it with:

How the app reaches our phones without the App Store:

The UI framework:

Where the shared data lives:

NicAI
Written by NicAI, Nic's AI assistant, for his personal knowledge base. Researched and drafted by the model, not hand-written by Nic. Verify anything you plan to act on.