AI Usage

Claude Code + Codex usage on your Mac, iPhone, and Apple Watch.

LIVEAI Tool
macOSiOS

What this project demonstrates

  • Native macOS WidgetKit extension shipped as a notarized DMG, with matching iPhone and Apple Watch apps, widgets, and complications
  • Mac-as-sole-writer CloudKit sync: a single latest UsageSnapshot record drives read-only iPhone and Watch mirrors with no merge conflicts
  • Embedded WKWebView with WKHTTPCookieStoreObserver to capture claude.ai sessionKey
  • Cross-process state sharing without App Groups by writing into the widget extension's own sandbox container
  • Honest token accounting that excludes Claude Code cache re-reads to report real new-token volume
  • Three-state provider detection (not installed / needs auth / connected) driving both the host setup UI and the widget layout
  • XcodeGen-driven project with a single release.sh that produces a signed, hardened-runtime, notarized, stapled DMG
  • Five-minute timeline policy with last-good fallback so the rings never go blank when an API blips

AI Usage is what happens when a usage widget is treated as a real native app instead of a webview wrapper. Live plan data on Mac, iPhone, and wrist, real Apple-platform plumbing, zero shortcuts.

AI Usage is for people who burn through Claude Code and Codex sessions all day and want a glance, not a webpage, to tell them where they stand. On the Mac the ring on the left fills with your current Claude session, the ring on the right with your Codex five-hour window. The Large variant adds weekly all-models, Sonnet only, Claude Design, Codex weekly, the rolling extra-usage spend against your monthly cap, and a seven-day token sparkline per provider. The same surfaces are now mirrored on iPhone and Apple Watch, so when you are away from the desk your wrist still shows the session and weekly rings, the reset timers, and the sparkline.

The numbers are real, and they are honest. Claude data comes from claude.ai/api/organizations/.../usage. Codex data comes from chatgpt.com/backend-api/codex/usage. There is no scraping, no shell-out to a CLI, no estimating from local jsonl. Token totals deliberately exclude cache re-reads, because Claude Code re-reads the whole context every turn and counting that would balloon the number into the billions, so what you see is real new-token volume. The host app does the network work every five minutes and writes the result into a JSON cache the widget reads on its next timeline refresh. If a request fails, the widget keeps showing the last good values and a local jsonl scan is used as a fallback so the rings never go blank.

Authentication for claude.ai was the interesting problem. There is no public token, so the host app opens claude.ai/login inside a WKWebView, observes the WKHTTPCookieStore, and grabs the sessionKey cookie the moment login completes. The cookie lives in the macOS Keychain. No copy-pasting cURL out of devtools, no manual session refresh, and Google SSO works in the embedded view the way it does in Safari. Codex is even simpler. After codex login on the terminal, the bearer token sits in ~/.codex/auth.json and the app reads it directly. Zero setup.

Getting that data onto the iPhone and Watch is where it gets interesting. The Mac is the only device that can see Codex data and run the embedded login, so it is the sole writer: at the end of every five-minute refresh it publishes a single UsageSnapshot record to your private CloudKit database, overwriting one latest record so there are no merge conflicts. The iPhone and Watch are read-only mirrors that fetch that record, with a local cache fallback and a hard timeout so the UI never hangs. No credentials ever leave the Mac. The phone gets a full app plus Home Screen widgets in all three sizes, and the watch gets a paged app plus circular and rectangular watch-face complications.

The non-obvious macOS challenge was sharing data between the unsandboxed host app and the sandboxed widget extension. App Groups would have been the textbook answer, but a wildcard Developer ID provisioning profile strips the App Groups entitlement, so the widget cannot read the shared container. The workaround is to skip App Groups entirely. The host writes the cache directly into the widget extension's own sandbox container at ~/Library/Containers/<widget-bundle-id>/Data/Library/Application Support/, and the widget reads its own files. No entitlement gymnastics, works on a personal Developer ID with no profile dance. The iPhone and Watch targets do use real provisioning profiles, because CloudKit requires the iCloud capability.

The setup screen is a single card with two rows. Each row shows a status dot, a "Connected / Not signed in / Not detected" label, the plan tier, and a contextual action button, with a run-at-login toggle driven by SMAppService. If Claude Code is not installed, the row collapses to a discreet "Install" link. Same for Codex. The widget mirrors this: a provider that is not installed is hidden from the layout entirely rather than rendered with a placeholder. The companion preview pane lets you flip between Live, Both, Claude only, and Codex only across every widget size so you can audit every state without changing your real install.

AI Usage is signed with a Developer ID Application certificate, hardened-runtime enabled, and notarized plus stapled by Apple. The DMG opens cleanly on any Mac with no Gatekeeper warning. The whole repo is XcodeGen-driven, one project.yml describes the macOS host, the widget extension, and the iOS and watchOS targets, and a single release.sh produces a notarized DMG end to end.

What it does

Mac, iPhone, and Apple Watch

One Mac app feeds a matching iPhone Home Screen widget and app plus an Apple Watch app and watch-face complications, all synced over your private iCloud.

Three Widget Sizes

Small, Medium, and Large variants. The Large widget adds weekly bars, extra-usage spend, and a seven-day token sparkline per provider.

Live Plan Data

Polls claude.ai/api/organizations and chatgpt.com/backend-api/codex/usage every five minutes. Session, weekly, and extra-usage rings stay in sync without a manual refresh.

Honest Token Counts

Totals exclude cache re-reads, since Claude Code re-reads the whole context every turn, so the sparkline shows real new-token volume instead of billions.

One-Click claude.ai Sign-In

Embedded WKWebView observes the cookie store and captures sessionKey the moment login completes. No cURL, no devtools, no token paste.

Codex Auto-Detect

Reads the bearer token written by codex login from ~/.codex/auth.json. Zero in-app configuration.

Three-State Provider Detection

Each provider resolves to not-installed, needs-auth, or connected. Missing providers are hidden from the widget instead of shown as placeholders.

Signed and Notarized

Developer ID Application signing, hardened runtime, stapled Apple notarization ticket. The DMG opens with no Gatekeeper warning.

Comments

Built with

  • Swift
  • SwiftUI
  • WidgetKit
  • macOS
  • iOS
  • watchOS
  • CloudKit
  • WKWebView
  • Keychain Services
  • XcodeGen

A few related projects that share the same design sensibility and build approach.

Browse all products