Swift上的USB连接代理

Jac*_*nig 6 macos usb swift

是否有一个Swift代表可以让我的班级知道何时通过计算机的USB插入新设备?我想知道我的程序何时可以使用新设备.

jtb*_*des 13

Eric Aya的答案已经相当不错了,但这里有一个Swift 3改编版.我把大部分丑陋的东西都包在了一USBWatcher堂; 将自己设置为此对象的委托以接收通知.

您可以将以下内容复制/粘贴到操场中以查看其是否正常工作 - 该示例仅在连接/断开设备时将消息记录到控制台.

不幸的是,IOKit API没有得到与其他一些C API相同的Swift-ifying处理(例如CoreGraphics).io_name_t是一个笨重的元组而不是一个正确的结构,C结构的方式通常被导入Swift; io_object_t不是真正的参考类型,因此它无法利用ARC.也许将来这会改变 - 如果你想看到更好的Swift API,你应该提交增强请求.

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<USBWatcher>.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<io_name_t>.allocate(capacity: 1)
        defer { buf.deallocate(capacity: 1) }
        return buf.withMemoryRebound(to: CChar.self, capacity: MemoryLayout<io_name_t>.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() ?? "<unknown>")")
    }

    func deviceRemoved(_ device: io_object_t) {
        print("device removed: \(device.name() ?? "<unknown>")")
    }
}

let example = Example()
Run Code Online (Sandbox Code Playgroud)

  • @EricAya:不透明指针是一种通过 C 函数“隧道”引用类实例的方法。它通常与用作 C 回调且无法捕获上下文的函数/闭包结合使用。比较(我为自我广告道歉)http://stackoverflow.com/questions/33294620/how-to-cast-self-to-unsafemutablepointervoid-type-in​​-swift 和 http://stackoverflow.com/问题/33260808/swift-proper-use-of-cfnotificationcenteraddobserver-w-callback 是另一个演示用法的示例。 (2认同)
  • 我创建了[USBDeviceSwift](https://github.com/Arti3DPlayer/USBDeviceSwift)库,方便使用`IOKit.usb`和`IOKit.hid` (2认同)

aya*_*aio 3

这个答案对我有用/sf/answers/2505208611/但它需要一些调整,比如创建一个桥接头来导入一些特定的 IOKit 部件。

首先,将 IOKit.framework 添加到您的项目中(单击“链接的框架和库”中的“+”)。

然后创建一个新的空“.m”文件,无论其名称如何。然后 Xcode 会询问是否应该创建一个“桥接标头”。说“是”。

忽略“.m”文件。在 Xcode 刚刚创建的新“YOURAPPNAME-Bridging-Header.h”文件中,添加以下行:

#include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/hid/IOHIDKeys.h>
Run Code Online (Sandbox Code Playgroud)

现在您可以使用链接答案中的代码。这是一个简化版本:

class USBDetector {
    class func monitorUSBEvent() {
        var portIterator: io_iterator_t = 0
        let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
        let gNotifyPort: IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
        let runLoopSource: Unmanaged<CFRunLoopSource>! = IONotificationPortGetRunLoopSource(gNotifyPort)
        let gRunLoop: CFRunLoop! = CFRunLoopGetCurrent()
        CFRunLoopAddSource(gRunLoop, runLoopSource.takeRetainedValue(), kCFRunLoopDefaultMode)
        let observer = UnsafeMutablePointer<Void>(unsafeAddressOf(self))
        _ = IOServiceAddMatchingNotification(gNotifyPort,
                                              kIOMatchedNotification,
                                              matchingDict,
                                              deviceAdded,
                                              observer,
                                              &portIterator)
        deviceAdded(nil, iterator: portIterator)
        _ = IOServiceAddMatchingNotification(gNotifyPort,
                                              kIOTerminatedNotification,
                                              matchingDict,
                                              deviceRemoved,
                                              observer,
                                              &portIterator)
        deviceRemoved(nil, iterator: portIterator)
    }
}

func deviceAdded(refCon: UnsafeMutablePointer<Void>, iterator: io_iterator_t) {
    var kr: kern_return_t = KERN_FAILURE
    while case let usbDevice = IOIteratorNext(iterator) where usbDevice != 0 {
        let deviceNameAsCFString = UnsafeMutablePointer<io_name_t>.alloc(1)
        defer {deviceNameAsCFString.dealloc(1)}
        kr = IORegistryEntryGetName(usbDevice, UnsafeMutablePointer(deviceNameAsCFString))
        if kr != KERN_SUCCESS {
            deviceNameAsCFString.memory.0 = 0
        }
        let deviceName = String.fromCString(UnsafePointer(deviceNameAsCFString))
        print("Active device: \(deviceName!)")
        IOObjectRelease(usbDevice)
    }
}

func deviceRemoved(refCon: UnsafeMutablePointer<Void>, iterator: io_iterator_t) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

注意:deviceAddeddeviceRemoved需要是函数(而不是方法)。

要使用此代码,只需启动观察者:

USBDetector.monitorUSBEvent()
Run Code Online (Sandbox Code Playgroud)

这将列出当前插入的设备,并且在每个新的 USB 设备插入/拔出事件中,它将打印设备名称。