2016-08-17 4 views
5

컴퓨터의 USB를 통해 새 장치를 꽂을 때 내 수업에 알릴 대의원이 있습니까? 내 장치에 새로운 장치가 언제 제공되는지 알고 싶습니다.스위프트의 USB 연결 대리인

+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-브리징-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) { 
    // ... 
} 

참고 : deviceAddeddeviceRemoved 함수 (하지 방법) 할 필요가있다.

USBDetector.monitorUSBEvent() 

이것은 현재 연결 장치를 나열하며, 모든 새로운 USB 장치 플러그/언 플러그 이벤트에 장치 이름을 인쇄합니다 :

은 관찰자를 시작,이 코드를 사용합니다.

+1

이것은 Swift 2.2 용으로 작성된 것인가? 나는 이것을 Swift 3에서 작동 시키려고 노력하고 있지만, 아무런 도움이되지 못합니다./변경해야 할 아이디어가 있습니까? – simonthumper

+0

예, 신속한 3 예를 제공해주세요. 감사합니다. – Arti

+0

나는이 질문에 현상금을 설정했습니다. 흥미로운 Swift 3 답변을 얻길 바랍니다. – Moritz

10

에릭 아야의 답변은 이미 훌륭하지만, 여기에 스위프트 3의 적응이 있습니다. USBWatcher 클래스의 대부분의 추악한 것들을 싸서; 이 객체의 대리자로 설정하여 알림을받습니다.

놀이터에 다음을 복사/붙여 넣기하면 작동하는지 확인할 수 있습니다. 예를 들어 장치 연결/연결 해제시 콘솔에 메시지를 기록합니다.

IOKit API가 다른 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

멋진 대답, 덕분에 많은 편리한 작업을 위해 [USBDeviceSwift] (https://github.com/Arti3DPlayer/USBDeviceSwift) 라이브러리를 만들었습니다! 나는 예를 들어'감시자 = 관리되지 않는 .fromOpaque하자 난 정말, 여기에 메모리 관리를 이해해야합니다 (예를!) 아니에요. takeUnretainedValue()가'나에게 약간의 신비입니다. // 어쨌든, 정말 대단합니다. 특히 Playground 예제가 있으면 감사하겠습니다. 나는이 대답을 예제로 사용하여 이런 종류의 메모리 관리를 연구 할 것이다. 다시 한 번 감사드립니다! – 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-of-cfnotificationcenteraddobserver-w-callback은 사용법을 보여주는 또 다른 예제입니다. –

+0

@MartinR 그'bridge()'도우미는 매우 근사합니다! 나는 그렇게 생각하지 않았습니다. 오버로딩은 꽤 멋지게 만듭니다. IOKit.usb'와'IOKit.hid''와 편리한 작업 – jtbandes