makgunay/claude-swift-skills
global-hotkeys
System-wide keyboard shortcut registration on macOS using NSEvent monitoring (simple, app-level) and Carbon EventHotKey API (reliable, system-wide). Covers NSEvent.addGlobalMonitorForEvents and addLocalMonitorForEvents, CGEvent tap for keystroke simulation, Carbon RegisterEventHotKey for system-wide hotkeys, modifier flag handling (.deviceIndependentFlagsMask), common key code mappings, debouncing, Accessibility permission requirements (AXIsProcessTrusted), and SwiftUI .onKeyPress for in-app sh…
Design & UI
Use judgment
Installs
15
Stars
1
Security
No audit
Freshness
Unknown
Updated
—
First seen Feb 14, 2026
Installation
Project
Global
Entire repo
Entire repo (global)
$
npx skills add makgunay/claude-swift-skills --skill global-hotkeys
Copy
SKILL.md
Global Hotkeys — System-Wide Keyboard Shortcuts
Critical Constraints
❌ DO NOT use only addGlobalMonitorForEvents → ✅ Also add addLocalMonitorForEvents (global doesn't fire when YOUR app is focused)
❌ DO NOT forget Accessibility permission → ✅ Global event monitoring requires AXIsProcessTrusted()
❌ DO NOT compare raw modifierFlags → ✅ Mask with .deviceIndependentFlagsMask first
❌ DO NOT use NSEvent for system-wide hotkeys in sandboxed apps → ✅ Use Carbon RegisterEventHotKey for reliability
Decision Tree
In-app shortcut only?
└── SwiftUI .onKeyPress or .keyboardShortcut
System-wide, non-sandboxed?
├── Simple → NSEvent global + local monitors
└── Reliable → Carbon RegisterEventHotKey
System-wide, sandboxed (App Store)?
└── Carbon RegisterEventHotKey + Accessibility entitlement
Method 1: NSEvent Monitors (Simple)
import AppKit
class HotkeyManager {
private var globalMonitor: Any?
private var localMonitor: Any?
var onHotkey: (() -> Void)?
var modifiers: NSEvent.ModifierFlags = [.command, .shift]
var keyCode: UInt16 = 49 // Space
func start() {
globalMonitor = NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { [weak self] event in
self?.handleEvent(event)
}
localMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
self?.handleEvent(event)
return event
}
}
func stop() {
if let m = globalMonitor { NSEvent.removeMonitor(m) }
if let m = localMonitor { NSEvent.removeMonitor(m) }
}
private func handleEvent(_ event: NSEvent) {
let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
if flags == modifiers && event.keyCode == keyCode {
DispatchQueue.main.async { self.onHotkey?() }
}
}
}
Method 2: Carbon API (Reliable, System-Wide)
import Carbon
class CarbonHotkeyManager {
private var hotkeyRef: EventHotKeyRef?
private var eventHandler: EventHandlerRef?
var onHotkey: (() -> Void)?
func register(keyCode: UInt32, modifiers: UInt32) {
unregister()
var hotkeyID = EventHotKeyID()
hotkeyID.signature = OSType("HTKY".fourCharCodeValue)
hotkeyID.id = 1
var eventType = EventTypeSpec(
eventClass: OSType(kEventClassKeyboard),
eventKind: UInt32(kEventHotKeyPressed)
)
let handler: EventHandlerUPP = { _, event, userData in
let mgr = Unmanaged<CarbonHotkeyManager>.fromOpaque(userData!).takeUnretainedValue()
mgr.onHotkey?()
return noErr
}
InstallEventHandler(GetApplicationEventTarget(), handler, 1, &eventType,
Unmanaged.passUnretained(self).toOpaque(), &eventHandler)
RegisterEventHotKey(keyCode, modifiers, hotkeyID,
GetApplicationEventTarget(), 0, &hotkeyRef)
}
func unregister() {
if let ref = hotkeyRef { UnregisterEventHotKey(ref) }
if let h = eventHandler { RemoveEventHandler(h) }
}
}
extension String {
var fourCharCodeValue: FourCharCode {
utf8.reduce(0) { ($0 << 8) + FourCharCode($1) }
}
}
Carbon Modifier Constants
// Carbon modifier flags for RegisterEventHotKey
let cmdKey: UInt32 = UInt32(cmdKey) // 256
let shiftKey: UInt32 = UInt32(shiftKey) // 512
let optionKey: UInt32 = UInt32(optionKey) // 2048
let controlKey: UInt32 = UInt32(controlKey) // 4096
// Example: Cmd+Shift+Space
register(keyCode: 49, modifiers: UInt32(cmdKey) | UInt32(shiftKey))
Common Key Codes
Key
Code
Key
Code
Key
Code
Space
49
Return
36
Escape
53
Tab
48
Delete
51
A
0
S
1
D
2
F
3
J
38
K
40
L
37
SwiftUI In-App Shortcuts
// Keyboard shortcut on button
Button("Save") { save() }
.keyboardShortcut("s", modifiers: .command)
// Raw key press handler
.onKeyPress(.escape) { dismiss(); return .handled }
.onKeyPress(.upArrow) { moveUp(); return .handled }
.onKeyPress(.downArrow) { moveDown(); return .handled }
Accessibility Permission Check
func checkAccessibilityPermissions() -> Bool {
let options = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: true]
return AXIsProcessTrustedWithOptions(options as CFDictionary)
}
// Non-prompting check
func isAccessibilityGranted() -> Bool {
AXIsProcessTrusted()
}
Common Mistakes & Fixes
Mistake
Fix
Hotkey works everywhere except own app
Add local monitor alongside global monitor
Modifier comparison fails
Mask with .deviceIndependentFlagsMask
Hotkey fires twice
Debounce with timestamp check (0.3s threshold)
Doesn't work on first launch
Check/request Accessibility permission
References