I built a Mac app this week. It does one thing: it blocks your keyboard and mouse with a single click. That's it. The simplest possible utility.
Here's why that was surprisingly hard, and what I learned about Swift development along the way.
The problem
You know that thing where you need to clean your laptop keyboard, but you can't because every time you touch a key, it types something? Or when your cat decides your keyboard is the perfect place to sit? Or when you're giving a presentation and accidentally hit a key that opens something embarrassing?
I wanted a quick way to completely disable input without locking the screen. Still see what's happening, still hear audio, just no keyboard or mouse input until I unlock it.
Windows has built-in tools for this. macOS... doesn't, really. There are some third-party apps, but they're either outdated, require too many permissions, or cost money for something this simple.
So I built my own.
Learning Swift in public
I should admit upfront: this was my first real macOS app. I've done iOS development before, but building for Mac is different. The APIs are different, the UI paradigms are different, and menu bar apps have their own weird quirks.
SwiftUI made this possible. A few years ago, building a menu bar app would have meant diving deep into AppKit, NSStatusItem, and all sorts of legacy APIs. With SwiftUI, you can get a basic menu bar app running in maybe 20 lines of code:
@main
struct InputLockerApp: App {
var body: some Scene {
MenuBarExtra("InputLocker", systemImage: "lock") {
Button("Lock Input") { lockInput() }
Divider()
Button("Quit") { NSApp.terminate(nil) }
}
}
}
Of course, the actual locking part is where things get complicated.
The permission nightmare
To actually block keyboard and mouse events on macOS, you need to use something called CGEvent taps. These are low-level hooks that intercept input events before they reach applications. Very powerful, and therefore very restricted.
Your app needs Accessibility permissions. Not just any permissions—the user has to manually go to System Settings, find your app in the Accessibility list, and toggle it on. There's no way to prompt for this automatically. Apple really doesn't want apps intercepting input unless the user explicitly allows it.
This makes sense from a security perspective, but it creates a terrible onboarding experience. User downloads app → app doesn't work → user is confused → user has to navigate through settings → user finally enables permission → app works. That's a lot of friction for a simple utility.
I tried to make this as smooth as possible by showing a clear explanation on first launch and providing a button that opens the exact settings pane. But there's no way around the fact that users have to do manual work to make the app functional.
The actual implementation
The core of InputLocker is an event tap that intercepts keyboard and mouse events:
let eventTap = CGEvent.tapCreate(
tap: .cghidEventTap,
place: .headInsertEventTap,
options: .defaultTap,
eventsOfInterest: CGEventMask(mask),
callback: { _, _, event, _ -> Unmanaged? in
// Return nil to block the event
return isLocked ? nil : Unmanaged.passRetained(event)
},
userInfo: nil
)
When locked, we return nil from the callback, which tells the system to discard the event. When unlocked, we pass the event through unchanged. Simple in theory, tricky in practice.
One gotcha: you need to be careful about which events you intercept. Block everything and you can't even unlock the app. I had to add a special keyboard shortcut (Cmd+Shift+U) that always works, even when locked. Otherwise you'd need to force-quit the app if you forget how to unlock it.
Distribution headaches
Building the app was actually the easy part. Distributing it was surprisingly annoying.
Apple really wants you to distribute through the App Store. But the App Store review process for apps that use Accessibility APIs is notoriously slow and unpredictable. They might reject you because your app "duplicates built-in functionality" or because they don't understand why it needs those permissions.
So I went with direct distribution instead. Build the app, put it in a DMG, put the DMG on my website. Users who download it will see a scary "this app is from an unidentified developer" warning because I don't have an Apple Developer account (€99/year just to sign my apps? No thanks).
The workaround is to tell users to right-click the app and choose "Open" instead of double-clicking. This bypasses Gatekeeper for that specific app. Not ideal, but it works.
What I learned
SwiftUI is good enough for simple apps. I was worried about whether SwiftUI could handle macOS development, but for a menu bar utility like this, it's perfect. Fast to iterate, good documentation, and it just works.
Permissions are UX. The best code in the world doesn't matter if users can't figure out how to give your app the permissions it needs. I spent almost as much time on the permission onboarding flow as on the actual functionality.
Distribution outside the App Store is painful. Apple has made it deliberately difficult to distribute apps independently. I understand why (security), but it's frustrating for small utilities that don't justify the App Store overhead.
Sometimes you just need to build it yourself. I could have searched harder for an existing solution, but building InputLocker took one day and now I have exactly the tool I wanted. Plus I learned Swift/macOS development, which will be useful for future projects.
Download it
If you want to try InputLocker, it's available on my website as a free download. It's unsigned (sorry), so you'll need to do the right-click-open dance. But once it's running, it does exactly what it says: locks your input with one click.
The code is on GitHub if you want to build it yourself or see how it works. PRs welcome, especially if you find bugs I missed.
Now if you'll excuse me, I need to clean my keyboard. InputLocker, activate.