A lightweight macOS Menu Bar app that silently counts your keystrokes — privately, locally, and with minimal resource usage.
TypingCounter lives quietly in your macOS Menu Bar and counts every keydown event in real time. No text is captured — only the count.
- 📊 1 Day view — See your keystroke breakdown by hour for today
- 📅 7 Days / 30 Days view — Track your daily keystroke totals over the past week or month
- 🔒 Privacy-first — Zero text capture, zero network calls, fully local
- ⚡ Lightweight — Counts are batched to disk every 10s instead of on every keystroke
- 🚀 Launch at Login — Optional toggle to start automatically on sign-in
- 🛡️ Reset confirmation — Clearing today's count requires a second confirmation
| What TypingCounter does | What it does NOT do |
|---|---|
| ✅ Counts keydown events | ❌ Capture or log typed text |
| ✅ Stores history locally on your Mac | ❌ Send any data over the network |
| ✅ Shows count in Menu Bar | ❌ Access clipboard or app content |
Download the latest .app from Releases and drag it to your Applications folder.
This project uses swiftc + Charts directly, without an Xcode project.
- Open the folder in VSCode
- Run the existing
swiftc-based build script - Launch the generated macOS app bundle
- Rebuild after any source changes
- Open Xcode → File → New → Project
- Choose macOS → App (SwiftUI, Swift)
- Set deployment target to macOS 13.0+
- Add all source files (
TypingCounterApp.swift,AppDelegate.swift,Core/*,Models/*,Views/*) to the app target - Ensure
@mainis set inTypingCounterApp.swiftwith@NSApplicationDelegateAdaptor(AppDelegate.self)
TypingCounter only needs Accessibility to function — the global keydown monitor is built on an API that doesn't require Input Monitoring.
System Settings → Privacy & Security → Accessibility → Enable TypingCounter
The app polls for this permission automatically, so counting starts on its own once granted — but if you enable it while the app is already running, quitting and relaunching once is the most reliable way to pick it up.
⚠️ If running from Xcode during development, grant permission to Xcode instead.
<key>LSUIElement</key>
<true/>
<key>NSInputMonitoringUsageDescription</key>
<string>TypingCounter needs Input Monitoring permission to count keydown events globally.</string>
<key>NSAppleEventsUsageDescription</key>
<string>TypingCounter may use Apple Events for accessibility-related automation support.</string>LSUIElement = truehides the Dock icon and keeps the app in the Menu Bar only.NSInputMonitoringUsageDescriptionis present for informational purposes but isn't actually gated on — see Permissions Required above.
TypingCounter/
├─ AppDelegate.swift # Menu bar status item + popover lifecycle
├─ TypingCounterApp.swift # SwiftUI app entry point
├─ Core/
│ ├─ CounterManager.swift # Main counting/state orchestration
│ ├─ KeyboardMonitor.swift # Global keydown monitoring
│ ├─ HistoryStore.swift # Local history persistence (batched writes)
│ └─ DateUtil.swift # Shared date formatting helper
├─ Models/
│ ├─ DailyRecord.swift # Daily count data model
│ └─ HourlyRecord.swift # Hourly count data model
├─ Views/
│ ├─ PopoverView.swift # Main popover UI
│ ├─ StatsView.swift # Stats chart UI
│ └─ HistoryChartView.swift # 1-day/7-day/30-day chart rendering
└─ README.md
Counter doesn't increase
- Check Accessibility is enabled for TypingCounter in System Settings
- If using Xcode, grant permission to Xcode too
- Relaunch the app after granting permission — it also polls automatically every few seconds, so it should pick this up on its own without a relaunch in most cases
Some keypresses don't count
- Arrow keys, function keys, media keys, and shortcut keys are intentionally ignored
- Some apps don't expose a focused text element clearly, so the keypress is skipped
Menu bar icon not visible
- Confirm
LSUIElementistruein Info.plist - Verify
NSStatusItemis created insideapplicationDidFinishLaunching
Popover doesn't open
- Ensure the status item button has
actionandtargetset - Confirm the popover content view/controller is initialized
Works once, then stops counting
- Check event monitor lifecycle — make sure the monitor reference is retained and not deallocated
- Re-check permissions if the app bundle ID changed
- Counts keydown events only — no awareness of what was typed
- Requires user-granted macOS privacy permissions
- Local and single-device only (no sync)
- Keyboard layout / IME behavior may affect event semantics
- Per-app keystroke breakdown (still text-free)
- Weekly/monthly stats — 7-day and 30-day views
- Export anonymized count metrics (local file only)
- Better onboarding flow for permissions — auto-poll + direct link to Accessibility settings
- Health indicator for monitoring state — green/orange status label in the popover
MIT © Joe NG


