Swift中是否有代表可以让我的班级知道何时通过计算机的USB插入新设备?我想知道我的程序何时可以使用新设备。
答案 0 :(得分:12)
USBWatcher
班里;将自己设置为此对象的委托以接收通知。
您可以将以下内容复制/粘贴到游乐场以查看其是否正常工作 - 该示例仅在设备连接/断开连接时将消息记录到控制台。
令人遗憾的是,IOKit API没有获得与其他一些C API相同的Swift-ifying处理(例如CoreGraphics)。 io_name_t
是一个笨重的元组而不是一个正确的结构,C结构的方式通常被导入Swift; io_object_t
不是真正的参考类型,因此无法利用ARC。也许在将来这会发生变化 - 如果您希望看到更好的Swift API,您应该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<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()
答案 1 :(得分:2)
这个答案对我有用https://stackoverflow.com/a/35788694,但它需要一些改编,比如创建一个桥接标题来导入一些特定的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>
现在您可以使用链接答案中的代码。这是一个简化版本:
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) {
// ...
}
注意:deviceAdded
和deviceRemoved
需要是函数(而不是方法)。
要使用此代码,只需启动观察者:
USBDetector.monitorUSBEvent()
这将列出当前插入的设备,并在每个新的USB设备插件/拔出事件中,它将打印设备名称。