2016-08-17 3 views
5

Есть ли делегат в Swift, который позволил бы моему классу узнать, когда новые устройства подключены через USB-порт компьютера? Я хотел бы знать, когда новое устройство станет доступным для моей программы.Делегат USB-соединения на Swift

+0

Ищите «NSWorkspace» Ключи пользователя. [link] (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/#//apple_ref/doc/constant_group/Volume_Mounting_Notification_User_Info_Keys) – Khundragpan

+0

Собственно, ваш код будет делегатом , методы которого будут вызываться в разные моменты времени. Я не уверен, что называется владельцем делегата, хотя – Alexander

+0

Возможный дубликат http://stackoverflow.com/questions/34628464/how-to-implement-ioservicematchingcallback-in-swift/39662693 – jtbandes

ответ

2

Этот ответ сработал для меня https://stackoverflow.com/a/35788694, но для этого потребовалась адаптация, например, создание заголовка моста для импорта определенных частей IOKit.

Сначала добавьте в проект проект IOKit.framework (нажмите «+» в «Связанные структуры и библиотеки»).

Затем создайте новый пустой файл «.m», независимо от его имени. Затем Xcode спросит, должен ли он сделать «мостовой заголовок». Скажи да.

Игнорировать файл «.m». В новом файле «YOURAPPNAME-Bridging-header.h», что Xcode только что создали, добавьте следующие строки:

#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 устройства/отсоединяйте случае он будет выводить имя устройства.

+1

Это написано для Swift 2.2? Я пытаюсь заставить это работать в Swift 3, но безуспешно:/Любые идеи об изменениях, которые нужно будет сделать? – simonthumper

+0

Да, просьба указать пример Swift 3. Спасибо – Arti

+0

Я поставил щедрость на этот вопрос - будем надеяться, что мы получим интересный ответ Свифта 3. – Moritz

10

Ответ Эрика Айи уже неплохой, но вот адаптация Swift 3. Я завернул большую часть уродливых вещей в классе USBWatcher; задайте себя как делегат этого объекта для получения уведомлений.

Вы можете скопировать/вставить следующее на игровое поле, чтобы увидеть, как оно работает - пример просто регистрирует сообщение на консоли, когда устройства подключены/отключены.

К сожалению, API-интерфейсы IOKit не получили того же метода Swift-ifying, что и другие C API-интерфейсы (например, 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() 
+0

Удивительный ответ, спасибо большое! Я не уверен, что я действительно понимаю управление памятью здесь, например 'let watcher = Unmanaged .fromOpaque (экземпляр!). TakeUnretainedValue()' для меня немного таинственный. // В любом случае, это действительно здорово, и я особенно ценю, что есть пример игровой площадки. В качестве примера я изучу этот вид управления памятью, используя этот ответ. Еще раз спасибо! :) – Moritz

+1

@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 для другого примера, который демонстрирует использование. –

+0

@MartinR Эти 'bridge()' помощники довольно круты! Я не думал об этом. Перегрузка делает это довольно приятным. – jtbandes

Смежные вопросы