NextMeeting — a macOS Menu Bar Meeting Countdown
Project notes: a native macOS menu bar app with a live countdown to your next meeting and a global shortcut to join instantly. Swift, EventKit, zero dependencies.
Repo: nostrapollo/next-meeting-menu-bar · MIT · ~1,300 lines of Swift
A macOS menu bar app that shows a live countdown to your next meeting — 15m: Team Standup — and joins it with a global keyboard shortcut (Cmd+Shift+J) from anywhere. No clicking through calendar apps, no hunting for the Zoom link at 10:59.
The Problem
The two seconds before a meeting are the worst-designed moment in remote work. The link is buried in a calendar event, behind a notification you dismissed, inside a notes field. Every meeting starts with the same scramble. The fix is embarrassingly simple: the next meeting and its join link should always be one keystroke away.
How It Works
Three services, one model, SwiftUI views. No third-party dependencies — pure Apple frameworks.
- CalendarService: reads local calendars via EventKit, fetches events in a configurable lookahead window (12/24/48h), filters all-day events, publishes the sorted meeting list.
- URL extraction: regex patterns over three event fields (URL, location, notes) matching Zoom (including vanity subdomains), Google Meet, Teams, WebEx, Whereby, Around, Discord, Slack Huddles, and Jitsi. Falls back to the event’s raw URL field.
- KeyboardShortcutService: registers the global Cmd+Shift+J hotkey (needs Accessibility permission), joins the current meeting if one is running, otherwise the next one.
- Meeting model: owns the countdown formatting (
2h 15m,<1m,Now) and the menu bar title truncation.
Design Decisions
- Local-only, by construction. Calendar data never leaves the machine. No analytics, no telemetry, no network calls except opening the meeting URL you click. The privacy claim is checkable: there’s no networking code to audit around.
- Zero dependencies. EventKit for calendars, SMAppService for launch-at-login, Carbon hotkeys for the shortcut. Nothing to vendor, nothing to update, nothing to trust.
- Regex over API integrations. Meeting platforms come and go; their URL shapes are stable. Pattern-matching the text fields covers nine platforms in ~10 lines each, and adding a new one is a one-line PR.
- Alert de-duplication. Full-screen alerts track already-alerted meeting IDs and clean the set against live events, so a rescheduled meeting alerts again but a refresh doesn’t re-fire.
What I’d Point At as the Interesting Bit
The meetingToAlert logic is where the small design tensions live: “alert at start” means within 60 seconds after start (you want it to fire even if the refresh tick lands late), while “alert N minutes before” is a one-minute-wide window checked against the refresh interval. Getting alerts to fire exactly once, on time, across restarts, with user-configurable timing — in a polling app — is fiddlier than the feature list suggests.
Status
Built January–March 2026. In daily use on my own Mac. macOS 13+, needs Calendar and Accessibility permissions. Build with ./build.sh or open in Xcode.