Web Development

Why I'm Obsessed With USB Hotplug Detection (And Why You Should Be Too)

A

Admin User

Author

Jun 18, 2026
5 min read
17 views
Why I'm Obsessed With USB Hotplug Detection (And Why You Should Be Too)

Last month, I spent three hours debugging a file sync application because my refresh button wasn't working. Not because the button had a bug, but because I'd built the entire feature around expecting users to click it. When a USB device connected, nothing happened until someone manually triggered the sync. It was friction I'd normalized so completely that I didn't even see it anymore.

Then I saw someone using an app that just... worked. They plugged in their phone, and within a second the UI updated. No button. No "try again." No asking themselves, "did it actually detect this?" That's when I realized I'd been shipping a half-finished experience.

Reading about how to properly handle USB hotplug detection on macOS—specifically in Rust—made me understand why that gap exists for so many developers. It's not that the solution is impossible; it's that most of us never dig deep enough into the platform-specific APIs to implement it right. I want to walk through what I've learned about doing this properly.

The Problem Nobody Talks About

Here's the thing about device detection: it's not sexy work. It doesn't ship features, it doesn't impress in a demo, and it doesn't go in a changelog. But it's the difference between an app that feels native and responsive versus one that feels... reluctant.

I've built integrations with external hardware before. Mobile phones, Arduinos, USB adapters. Every time, I defaulted to some version of polling—a timer that periodically checks for connected devices. It works, it's simple, and it burns battery life and CPU cycles while making everything feel slightly delayed.

The right way is hotplug detection: telling the OS, "Hey, let me know the instant a USB device connects or disconnects." On macOS, that means tapping into IOKit, Apple's kernel framework for hardware communication.

How Rust + nusb Makes This Practical

The original article breaks down using nusb, a Rust library that wraps IOKit's USB notifications. Instead of constantly asking "are you there yet?", you subscribe to an event stream and react when something actually happens.

The pattern is straightforward: spawn a dedicated thread that blocks on a watch iterator, match against connect/disconnect events, and emit those events up to your application layer. On macOS, IOKit handles all the hard work of monitoring the USB bus; you're just listening.

What I find elegant about this approach is the separation of concerns. Your hardware detection thread doesn't need to know anything about your UI. It just says "device X connected" and your app decides what to do with that information. In Tauri (an Electron alternative for Rust desktop apps), that means emitting an event to the frontend.

The Two-Step Verification That Changed My Mind

Here's where the original article's approach gets smart, and where I initially had doubts: they don't just trust the USB vendor ID. They verify with ADB (Android Debug Bridge) afterward.

At first, I thought this was overkill. You catch a USB connection, you look at the vendor ID, done. But then I thought about the edge cases: what if someone has an external hard drive from a Chinese manufacturer that used a vendor ID that collides? What if there's a USB hub situation causing false positives?

The author's approach is vendor ID for speed (instant visual feedback), then ADB for accuracy (don't actually try to sync with a printer). This is pragmatic design—give the user something responsive while you verify in the background.

fn watch_usb_devices() {
    std::thread::spawn(move || {
        let watch = nusb::watch_devices().expect("Failed to start USB watch");
        for event in watch {
            match event {
                HotplugEvent::Connected(device_info) => {
                    if ANDROID_VENDOR_IDS.contains(&device_info.vendor_id()) {
                        // Fast feedback to user
                        println!("Device connected: {}", 
                                 device_info.product_string().unwrap_or_default());
                        
                        // Verify with ADB in background
                        if confirm_with_adb().await {
                            emit_safe_event("device-confirmed");
                        }
                    }
                }
                HotplugEvent::Disconnected(_) => {
                    emit_safe_event("device-disconnected");
                }
            }
        }
    });
}

The blocking iterator means this thread is sleeping when nothing's happening. It only wakes up when IOKit detects actual USB events. Compare that to a polling loop that wakes up every 500ms, and suddenly the battery and CPU impact makes sense.

My Real Take

I love this article because it's rooted in shipping real apps. The author shipped seven macOS applications as a solo developer—that's not theoretical, that's battle-tested. They know which approach is worth the implementation effort because they've felt the user impact.

Would I implement this exactly the same way? Maybe not. I'd probably want to add retry logic for ADB confirmation and better error handling when the watch thread encounters issues. But the core philosophy—"use platform notifications, verify for safety, emit to your app layer"—that's solid.

The thing I'm stealing immediately: the two-step approach. Fast feedback from hardware, confirmed by software. That's the pattern.

What Would You Do?

Have you built device detection into a desktop app? What did you choose—polling, platform notifications, or something else entirely? I'm curious whether other developers have hit the same friction I did, and what made them finally care about getting it right.

Source: This post was inspired by "USB Hotplug Detection in Rust on macOS — Reacting to Device Connect/Disconnect" by Dev.to. Read the original article

Share this article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

We Built an AI Code Tool. Then Reality Hit.
Web Development Jul 16

We Built an AI Code Tool. Then Reality Hit.

Six months ago, my team shipped an AI-powered code suggestion feature. The demos were clean. The founder was excited. We had benchmarks showing 85% accuracy on test cases. Then a client tried it on their actual codebase—a three-year-old Next.js monorepo with custom tooling, weird...