NSEvent Global Monitor in Sandboxed macOS Apps
Does NSEvent.addGlobalMonitorForEvents work in app sandbox macOS apps with accessibility permission? Explore development quirks, App Store risks, and CGEventTap alternative for reliable global keyboard monitoring in SwiftUI utilities.
Does NSEvent.addGlobalMonitorForEvents work in sandboxed macOS apps with Accessibility permissions? App Store approval concerns for global keyboard monitoring in SwiftUI
I’m building a SwiftUI macOS utility that records and displays global keyboard shortcuts (e.g., Cmd+C, Cmd+V) using NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]).
Observed behavior:
- In a fresh project with App Sandbox disabled, requesting Accessibility permissions via
AXIsProcessTrustedWithOptionsworks perfectly. - After re-enabling App Sandbox in Signing & Capabilities, it still captures global key events from other apps.
- Tested in a new project with sandbox enabled by default—it also works after granting Accessibility access.
Questions:
- Is this expected? Does granting manual Accessibility permissions allow sandboxed apps to use global event monitors?
- Does macOS “remember” prior non-sandboxed trust for the same Bundle ID?
- For App Store submission: Will Apple reject a sandboxed app using this for user productivity tracking (displaying shortcuts with explicit permission)?
Key code snippet:
eventMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]) { event in
print("Captured: (event.charactersIgnoringModifiers ?? \"\")")
}
Full implementation details:
- Checks
AXIsProcessTrustedWithOptionsbefore starting. - Processes modifiers (⌘, ⇧, ⌥, ⌃) and keys only for shortcuts.
- Cleans build folder, restarts, revokes permissions—still works.
Is this reliable for production/App Store, or a development glitch?
NSEvent.addGlobalMonitorForEvents can capture global key events like Cmd+C in app sandbox-enabled macOS apps after granting accessibility permission, but this behavior is quirky in development and not officially recommended for sandbox macOS production or App Store submission. Apple forums and docs point to accessibility permission tying monitors to trusted processes, yet sandboxed apps often work due to macOS quirks—though relying on it risks rejection. Switch to CGEventTap with Input Monitoring for reliable, App Store-friendly global keyboard monitoring in SwiftUI utilities.
Contents
- How NSEvent Global Monitors Work
- Accessibility Permissions and Sandbox Gotchas
- Why It “Works” in Development
- App Store Approval Risks
- Better Alternative: CGEventTap + Input Monitoring
- Implementation Steps
- Sources
- Conclusion
How NSEvent Global Monitors Work
Your code snippet looks solid—NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]) grabs copies of key events posted system-wide, printing stuff like “c” for Cmd+C. But here’s the catch: these monitors don’t modify or block events; they just observe. Apple’s official docs confirm this setup receives events from other apps, making it handy for productivity tools tracking shortcuts.
Why does it need special perms? Key events are sensitive. Without trust, nothing fires. You nailed the check with AXIsProcessTrustedWithOptions, prompting users to System Settings > Privacy & Security > Accessibility. Once granted, it flows.
In non-sandboxed apps? Perfect, as you’ve seen. But flip on app sandbox…
Accessibility Permissions and Sandbox Gotchas
Sandbox macOS adds isolation—files, network, IPC all locked down. Does accessibility permission punch through for NSEvent monitors? Technically, yes, but it’s messy.
Apple docs state outright: key event monitors via NSEvent require the app to be “trusted for accessibility.” Sandbox doesn’t outright block this, but forums reveal the rub. One thread nails it: NSEvent global monitors demand Accessibility, while alternatives like CGEventTap use Input Monitoring—which is sandbox-friendly.
Your tests match reports: sandbox enabled, clean build, revoke perms, re-grant—still captures. Expected? Kinda. macOS links trust to the process, not sandbox state directly. But Apple devs warn: don’t bank on it for production.
Ever notice it “just works” post-revoke? That’s macOS caching the bundle’s trust. Change Bundle ID or nuke DerivedData, and it flakes.
Why It “Works” in Development
You asked: does macOS remember non-sandboxed trust for the same Bundle ID? Spot on—yes, often. Security prefs tie to the app’s code signature and path. Dev builds (even sandboxed) reuse signatures from prior runs.
Stack Overflow devs report the same: fresh project with sandbox? Grant Accessibility, events flow. Revoke and restart? Sometimes sticks due to TCC (Transparency, Consent & Control) database quirks. But production signing flips this—App Store apps get hardened review.
Is it a glitch? More like an undocumented edge. Forums confirm: sandboxed NSEvent works in Xcode, fails reliably in distributed .app without tweaks. Your “full implementation details” (modifiers, cleaning builds) scream reliable testing, but scale to release build. It’ll bite.
Quick test: Archive > Export unsigned, run outside Xcode. Poof—issues surface.
App Store Approval Risks
Big question: App Store for a SwiftUI shortcut tracker? Global keyboard monitoring isn’t banned—Macworld notes hotkeys stay welcome. But NSEvent + Accessibility? Risky.
Apple forums hammer: use least privilege. Accessibility is broad (screen reading, UI tweaks); Input Monitoring is scoped for keys. Reviewers flag overreach—especially sandboxed apps pretending it’s fine.
Guideline 2.4.5? Hotkeys ok, but explain why in submission notes: “Tracks user shortcuts for productivity display, with explicit Input Monitoring consent.” Your utility sounds legit—no spying, just Cmd+V vibes.
Rejection stories? Sparse, but GitHub issues cite scrutiny for Accessibility abuse. Play safe: swap APIs.
Better Alternative: CGEventTap + Input Monitoring
Ditch NSEvent. Apple pushes CGEventTap for sandboxed global keys. Why? Input Monitoring perm is App Store-ok, even sandboxed.
Forums glow: “CGEventTap requires Input Monitoring, not Accessibility—perfect for sandbox.” WWDC echoes: post-10.15, it’s the way.
SwiftUI stub:
import CoreGraphics
let eventMask = (1 << CGEventType.keyDown.rawValue)
guard let eventTap = CGEvent.tapCreate(
tap: .cgSessionEventTap,
place: .headInsertEventTap,
options: .defaultTap,
eventsOfInterest: CGEventMask(eventMask),
callback: { proxy, type, event, refcon in
print("Key: (event.getIntegerValueField(.keyboardEventKeycode))")
return Unmanaged.passUnretained(event)
},
userInfo: nil
) else { return }
let runLoopSource = eventTap.createRunLoopSource()
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)
eventTap.start()
Prompt Input Monitoring via IOHIDRequestAccess or just let user add in Settings. Sandbox? Golden.
Implementation Steps
Ready to productionize? Checklist:
- Perm check: Use
AXIsProcessTrustedfor Accessibility (NSEvent) or detect CGEventTap failure for Input. - Prompt user:
AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt: true]). - SwiftUI integration: Wrap in
ObservableObject, toggle monitor inonAppear. - Edge cases: Ignore self-events (
event.focusInvalWindow == nil). Modifiers?event.flags. - App Store prep: Notes: “Uses Input Monitoring to display pressed shortcuts—user must approve in Privacy settings. No data leaves app.”
- Test release:
productbuild --sign, install, revoke perms.
NSEvent for dev? Fine. Production? CGEventTap wins.
Sources
- Monitoring Events - Apple Documentation
- addGlobalMonitorForEvents(matching:handler:) - Apple Developer Documentation
- Accessibility Permission In Sandbox For Keyboard - Apple Developer Forums
- Accessibility permission in sandboxed app - Apple Developer Forums
- Simulate and send keyboard events — Apple Developer Forums
- Adding a global monitor with NSEventMaskKeyDown mask does not trigger - Stack Overflow
- addGlobalMonitorForEventsMatchingMask not working - Stack Overflow
- Which API is behind the privacy feature “Input Monitoring” - Stack Overflow
- Investigate using CGEvent.tapCreate for global hotkeys - GitHub Issue (AeroSpace)
- Apps using global hotkeys will remain welcome in the Mac App Store - Macworld
Conclusion
App sandbox with NSEvent works in dev thanks to Accessibility quirks, but skip it for sandbox macOS App Store apps—opt for CGEventTap and Input Monitoring to dodge rejections. Your shortcut tracker? Totally viable with clear perms and notes. Test release builds religiously; macOS trust ain’t forever. Build smart, ship safe.