---
title: Communicating with human interface devices
framework: corehid
role: article
role_heading: Article
path: corehid/communicatingwithhiddevices
---

# Communicating with human interface devices

Interact with and obtain data from devices such as keyboards and mice.

## Overview

Overview To communicate with a human interface device (HID), you must identify and match it using a set of matching critiera. When you match the device, you become its client and can query its properties and capabilities. note: Interacting with certain HIDs, such as keyboards, require user approval. Ensure that you grant permission to access the device to use it. Locate the device of interest To locate the device you’re interested in, specify a set of matching criteria using HIDDeviceManager.DeviceMatchingCriteria. The following code uses this method to discover the Magic Keyboard with Numeric Keypad product: let matchingCriteria = HIDDeviceManager.DeviceMatchingCriteria(primaryUsage: .genericDesktop(.keyboard), product: "Magic Keyboard with Numeric Keypad") Create a HIDDeviceManager and provide the criteria to monitorNotifications(matchingCriteria:). Notifications arrive immediately for connected devices, and later when newly connected matching devices appear. monitorNotifications returns an asynchronous stream that you iterate over. The loop awaits and releases the current Task until a notification comes in. let manager = HIDDeviceManager()

// The criteria combines multiple sets in a single array. for try await notification in await manager.monitorNotifications(matchingCriteria: [matchingCriteria]) {     switch notification {         case .deviceMatched(let deviceReference):             print("A device was found.")

case .deviceRemoved(let deviceReference):             print("A device was removed.")

default:             continue     } } To specify matching criteria using additional properties, see init(primaryUsage:deviceUsages:vendorID:productID:transport:product:manufacturer:modelNumber:versionNumber:serialNumber:uniqueID:locationID:localizationCode:isBuiltIn:extraProperties:). Communicate with the device Create a HIDDeviceClient and pass in the device reference to start communication with the device. Verify that you’re communicating with the correct device by checking the usage with primaryUsage, the transport with transport, and the product name with product. enum ExampleError : Error {     case deviceCreation     case wrongUsage     case wrongTransport     case wrongProduct     case noElement     case noValue     case valuesNotEqual }

guard let client = HIDDeviceClient(deviceReference: deviceReference) else {     throw ExampleError.deviceCreation }

let usage = await client.primaryUsage guard usage == .genericDesktop(.keyboard) else {     print("Wrong usage: \(usage)")     throw ExampleError.wrongUsage }

let transport = await client.transport guard transport == .bluetooth else {     print("Wrong transport: \(String(describing: transport))")     throw ExampleError.wrongTransport }

let product = await client.product guard product == "Magic Keyboard with Numeric Keypad" else {     print("Wrong product: \(String(describing: product))")     throw ExampleError.wrongProduct } Obtain the device’s input report using dispatchGetReportRequest(type:id:timeout:). Specify HIDReportType.input for the type, and provide the appropriate HIDReportID. This method initiates a get report request to the device over Bluetooth and returns the device’s response. let report = try await client.dispatchGetReportRequest(type: .input, id: HIDReportID(rawValue: 1)) print("Get report data: [\(report.map { String(format: "%02x", $0) }.joined(separator: " "))]") Monitor the device for input reports or other notifications by calling monitorNotifications(reportIDsToMonitor:elementsToMonitor:): for try await notification in await client.monitorNotifications(reportIDsToMonitor: [HIDReportID.allReports], elementsToMonitor: []) {     switch notification {

// Receive device input data.     case .inputReport(let reportID, let reportData, let timestamp):         print("Received input report ID:\(String(describing: reportID)) timestamp:\(timestamp) data:[\(reportData.map { String(format: "%02x", $0) }.joined(separator: " "))]")

// Receive a notification if another client seizes this device.     case .deviceSeized:         print("\(client) seized")

// Receive a notification if another client releases this device.     case .deviceUnseized:         print("\(client) unseized")

// Receive a notification if a person removes the client.     case .deviceRemoved:         print("\(client) removed")

default:         continue     } } Raw reports provide detailed information about data going to and from the device; however, this can generate more detail than you need. Instead, use HIDElement to obtain specific information from a device. For the keyboard in this example, the input report contains the report ID, status of the modifier keys, the currently pressed keys, vendor defined data and padding. To obtain just the state of the Shift key, obtain the corresponding element. let elements = await client.elements guard let leftShiftKey = elements.filter({ $0.usage == .keyboardOrKeypad(.keyboardLeftShift) }).first else {     throw ExampleError.noElement } With leftShiftKey, monitor HIDDeviceClient.Notification.elementUpdates(values:) for any changes using HIDElement.Value. Values also arrive as a byte stream in bytes, but are interpretable as integers using extensions such as integerValue(asTypeTruncatingIfNeeded:). Because the Shift key state is 1 bit, treat it as a UInt8. for try await notification in await client.monitorNotifications(reportIDsToMonitor: [], elementsToMonitor: [leftShiftKey]) {     switch notification {     case .elementUpdates(let values):         let leftShiftVal = values[0]         print("Left shift state: \(leftShiftVal.integerValue(asTypeTruncatingIfNeeded: UInt8.self))")

default:         continue     } } Use HIDElement with get and set reports through updateElements(_:timeout:). This takes HIDElement from HIDDeviceClient.RequestElementUpdate and HIDDeviceClient.ProvideElementUpdate, then issues get and set reports with the raw report data in the background. The following example creates a unit test for the keyboard. It turns off the Caps Lock LED, queries the state of the left Shift key, turns on the Caps Lock LED, then queries the state of the left Shift key again to determine if toggling the LED alters the state of the left Shift key. guard let capsLockLED = elements.filter({ $0.usage == .led(.capsLock) }).first else {     throw ExampleError.noElement }

let turnOffVal = HIDElement.Value(element: capsLockLED, fromIntegerTruncatingIfNeeded: 0, timestamp: SuspendingClock.now) let turnOnVal  = HIDElement.Value(element: capsLockLED, fromIntegerTruncatingIfNeeded: 1, timestamp: SuspendingClock.now)

let turnOffRequest     = HIDDeviceClient.ProvideElementUpdate(values:   [turnOffVal]) let stateRequestBefore = HIDDeviceClient.RequestElementUpdate(elements: [leftShiftKey]) let turnOnRequest      = HIDDeviceClient.ProvideElementUpdate(values:   [turnOnVal]) let stateRequestAfter  = HIDDeviceClient.RequestElementUpdate(elements: [leftShiftKey]) let requestResults     = await client.updateElements([turnOffRequest, stateRequestBefore, turnOnRequest, stateRequestAfter])

// Ensure that the set operations succeed. try requestResults[turnOffRequest]!.get() try requestResults[turnOnRequest]!.get()

// Read the initial state of the left Shift key. guard let leftShiftStateBefore = try requestResults[stateRequestBefore]?.get().first?.integerValue(asTypeTruncatingIfNeeded: UInt8.self) else {     throw ExampleError.noValue }

// Read the final state of the left Shift key. guard let leftShiftStateAfter = try requestResults[stateRequestAfter]?.get().first?.integerValue(asTypeTruncatingIfNeeded: UInt8.self) else {     throw ExampleError.noValue }

// Ensure the initial state didn't change as a result of the LED toggle. guard leftShiftStateBefore == leftShiftStateAfter else {     throw ExampleError.valuesNotEqual }

## See Also

### Interaction

- [HIDDeviceClient](corehid/hiddeviceclient.md)
- [HIDElement](corehid/hidelement.md)
- [HIDElementCollection](corehid/hidelementcollection.md)
- [HIDElement.Value](corehid/hidelement/value.md)
- [HIDElementUpdate](corehid/hidelementupdate.md)
- [HIDReportType](corehid/hidreporttype.md)
- [HIDReportID](corehid/hidreportid.md)
- [HIDUsage](corehid/hidusage.md)
- [HIDDeviceError](corehid/hiddeviceerror.md)
- [HIDDeviceTransport](corehid/hiddevicetransport.md)
- [HIDDeviceLocalizationCode](corehid/hiddevicelocalizationcode.md)
