USB Connection Delegate on Swift

后端 未结 2 1846
太阳男子
太阳男子 2020-12-03 06:03

Is there a delegate in Swift that would let my class know when new devices are plugged in via the computer\'s USB? I would like to know when a new device becomes available t

2条回答
  •  失恋的感觉
    2020-12-03 06:44

    Eric Aya's answer is already quite good, but here's a Swift 3 adaptation. I wrapped most of the ugly stuff in a USBWatcher class; set yourself as the delegate of this object to receive notifications.

    You can copy/paste the following into a playground to see it work — the example just logs a message to the console when devices are connected/disconnected.

    It's unfortunate that the IOKit APIs haven't gotten the same Swift-ifying treatment that some other C APIs have been (e.g. CoreGraphics). io_name_t is a clunky tuple instead of a proper struct, the way C structs are usually imported to Swift; io_object_t isn't a real reference type, so it can't take advantage of ARC. Perhaps in the future this will change — if you'd like to see a better Swift API, you should file an enhancement request.

    import Foundation
    import IOKit
    import IOKit.usb
    
    public protocol USBWatcherDelegate: class {
        /// Called on the main thread when a device is connected.
        func deviceAdded(_ device: io_object_t)
    
        /// Called on the main thread when a device is disconnected.
        func deviceRemoved(_ device: io_object_t)
    }
    
    /// An object which observes USB devices added and removed from the system.
    /// Abstracts away most of the ugliness of IOKit APIs.
    public class USBWatcher {
        private weak var delegate: USBWatcherDelegate?
        private let notificationPort = IONotificationPortCreate(kIOMasterPortDefault)
        private var addedIterator: io_iterator_t = 0
        private var removedIterator: io_iterator_t = 0
    
        public init(delegate: USBWatcherDelegate) {
            self.delegate = delegate
    
            func handleNotification(instance: UnsafeMutableRawPointer?, _ iterator: io_iterator_t) {
                let watcher = Unmanaged.fromOpaque(instance!).takeUnretainedValue()
                let handler: ((io_iterator_t) -> Void)?
                switch iterator {
                case watcher.addedIterator: handler = watcher.delegate?.deviceAdded
                case watcher.removedIterator: handler = watcher.delegate?.deviceRemoved
                default: assertionFailure("received unexpected IOIterator"); return
                }
                while case let device = IOIteratorNext(iterator), device != IO_OBJECT_NULL {
                    handler?(device)
                    IOObjectRelease(device)
                }
            }
    
            let query = IOServiceMatching(kIOUSBDeviceClassName)
            let opaqueSelf = Unmanaged.passUnretained(self).toOpaque()
    
            // Watch for connected devices.
            IOServiceAddMatchingNotification(
                notificationPort, kIOMatchedNotification, query,
                handleNotification, opaqueSelf, &addedIterator)
    
            handleNotification(instance: opaqueSelf, addedIterator)
    
            // Watch for disconnected devices.
            IOServiceAddMatchingNotification(
                notificationPort, kIOTerminatedNotification, query,
                handleNotification, opaqueSelf, &removedIterator)
    
            handleNotification(instance: opaqueSelf, removedIterator)
    
            // Add the notification to the main run loop to receive future updates.
            CFRunLoopAddSource(
                CFRunLoopGetMain(),
                IONotificationPortGetRunLoopSource(notificationPort).takeUnretainedValue(),
                .commonModes)
        }
    
        deinit {
            IOObjectRelease(addedIterator)
            IOObjectRelease(removedIterator)
            IONotificationPortDestroy(notificationPort)
        }
    }
    
    extension io_object_t {
        /// - Returns: The device's name.
        func name() -> String? {
            let buf = UnsafeMutablePointer.allocate(capacity: 1)
            defer { buf.deallocate(capacity: 1) }
            return buf.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size) {
                if IORegistryEntryGetName(self, $0) == KERN_SUCCESS {
                    return String(cString: $0)
                }
                return nil
            }
        }
    }
    
    
    import PlaygroundSupport
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    class Example: USBWatcherDelegate {
        private var usbWatcher: USBWatcher!
        init() {
            usbWatcher = USBWatcher(delegate: self)
        }
    
        func deviceAdded(_ device: io_object_t) {
            print("device added: \(device.name() ?? "")")
        }
    
        func deviceRemoved(_ device: io_object_t) {
            print("device removed: \(device.name() ?? "")")
        }
    }
    
    let example = Example()
    

提交回复
热议问题