Usb Connection Delegate on Swift

USB Connection Delegate on Swift

This answer worked for me https://stackoverflow.com/a/35788694 but it needed some adaptation, like creating a bridging header to import some specific IOKit parts.

First, add IOKit.framework to your project (click "+" in "Linked Frameworks and Libraries").

Then create a new empty ".m" file, whatever its name. Xcode will then ask if it should make a "bridging header". Say YES.

Ignore the ".m" file. In the new "YOURAPPNAME-Bridging-Header.h" file that Xcode just created, add the following lines:

#include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/hid/IOHIDKeys.h>

Now you can use the code in the linked answer. Here's a simplified version:

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) {
// ...
}

Note: deviceAdded and deviceRemoved need to be functions (not methods).

To use this code, just launch the observer:

USBDetector.monitorUSBEvent()

This will list the currently plugged devices, and on every new USB device plug/unplug event it will print the device name.

macOS - Get information from connected iOS device via USB

Thanks to @Robert's comment, I found out about libimobiledevice's ideviceinfo which does what I needed. I couldn't find a way to directly include it in my app, but I found the imobiledevice-net repo, which has macOS libraries on the release page.

These libraries can be used with the help of Swift's Process class.

How to implement IOServiceMatchingCallBack in Swift

Here's a Swift 3 version, using closures instead of global functions (a closure w/o a context can be bridged to a C function pointer), using GCD instead of Runloops (much nicer API), using callbacks and dispatches to inform about events and using real objects instead of static objects or singletons:

import Darwin
import IOKit
import IOKit.usb
import Foundation


class IOUSBDetector {

enum Event {
case Matched
case Terminated
}

let vendorID: Int
let productID: Int

var callbackQueue: DispatchQueue?

var callback: (
( _ detector: IOUSBDetector, _ event: Event,
_ service: io_service_t
) -> Void
)?


private
let internalQueue: DispatchQueue

private
let notifyPort: IONotificationPortRef

private
var matchedIterator: io_iterator_t = 0

private
var terminatedIterator: io_iterator_t = 0


private
func dispatchEvent (
event: Event, iterator: io_iterator_t
) {
repeat {
let nextService = IOIteratorNext(iterator)
guard nextService != 0 else { break }
if let cb = self.callback, let q = self.callbackQueue {
q.async {
cb(self, event, nextService)
IOObjectRelease(nextService)
}
} else {
IOObjectRelease(nextService)
}
} while (true)
}


init? ( vendorID: Int, productID: Int ) {
self.vendorID = vendorID
self.productID = productID
self.internalQueue = DispatchQueue(label: "IODetector")

guard let notifyPort = IONotificationPortCreate(kIOMasterPortDefault) else {
return nil
}

self.notifyPort = notifyPort
IONotificationPortSetDispatchQueue(notifyPort, self.internalQueue)
}

deinit {
self.stopDetection()
}


func startDetection ( ) -> Bool {
guard matchedIterator == 0 else { return true }

let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: vendorID)
matchingDict[kUSBProductID] = NSNumber(value: productID)

let matchCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let detector = Unmanaged<IOUSBDetector>
.fromOpaque(userData!).takeUnretainedValue()
detector.dispatchEvent(
event: .Matched, iterator: iterator
)
};
let termCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let detector = Unmanaged<IOUSBDetector>
.fromOpaque(userData!).takeUnretainedValue()
detector.dispatchEvent(
event: .Terminated, iterator: iterator
)
};

let selfPtr = Unmanaged.passUnretained(self).toOpaque()

let addMatchError = IOServiceAddMatchingNotification(
self.notifyPort, kIOFirstMatchNotification,
matchingDict, matchCallback, selfPtr, &self.matchedIterator
)
let addTermError = IOServiceAddMatchingNotification(
self.notifyPort, kIOTerminatedNotification,
matchingDict, termCallback, selfPtr, &self.terminatedIterator
)

guard addMatchError == 0 && addTermError == 0 else {
if self.matchedIterator != 0 {
IOObjectRelease(self.matchedIterator)
self.matchedIterator = 0
}
if self.terminatedIterator != 0 {
IOObjectRelease(self.terminatedIterator)
self.terminatedIterator = 0
}
return false
}

// This is required even if nothing was found to "arm" the callback
self.dispatchEvent(event: .Matched, iterator: self.matchedIterator)
self.dispatchEvent(event: .Terminated, iterator: self.terminatedIterator)

return true
}


func stopDetection ( ) {
guard self.matchedIterator != 0 else { return }
IOObjectRelease(self.matchedIterator)
IOObjectRelease(self.terminatedIterator)
self.matchedIterator = 0
self.terminatedIterator = 0
}
}

And here is some simple test code to test that class (set product and vendor ID as appropriate for your USB device):

let test = IOUSBDetector(vendorID: 0x4e8, productID: 0x1a23)
test?.callbackQueue = DispatchQueue.global()
test?.callback = {
(detector, event, service) in
print("Event \(event)")
};
_ = test?.startDetection()
while true { sleep(1) }


Related Topics



Leave a reply



Submit