Convert UTF-16 CFString to String?

I'm calling code like this:

	if let ps = IOHIDDeviceGetProperty(self.device, "Product" as CFString) as? String
	{
		debugLog("Product: \(ps)")
	}

Expecting IOHIDDeviceGetProperty() to return a UTF-16-encoded CFStringRef. But I think Swift assumes things are UTF-8. How do I do convert this, or cause the string to be created with the right encoding?

I tried setting the string on the USB device to be UTF-8 encoded, and it’s not returning the right string in that case, either. I'm not sure where the disconnect lies.

I don't know what the right string is you mentioned. If you have a String that's not stored using using UTF-8 encoding, you can use makeContiguousUTF8 or withUTF8.

Neither one of those takes an encoding. How does it know the underlying encoding?

Swift will automatically convert from NSString encodings to UTF8 as part of the bridging process, either at the time of the as? or lazily on access.

1 Like

Okay, perhaps IOHIDDeviceGetProperty() is failing to encode the CFString it returns correctly.

What is failing here? More importantly, what is returned by the IOHIDDeviceGetProperty? CFString can normally convert to String, even dynamically:

let x: CFTypeRef = "Test" as CFString

if let x = x as? String {
    print(x) // Test
}

And it's likely not about encoding. String supports other bridged formats by default (at a cost of lower speed). Otherwise, we wouldn't have makeContiguousUTF8.

Here's the background, sorry it's getting pretty off-topic for Swift; I had initially just tried to see if something was failing in the Swift-C bridge. I'm building a little USB HID device, and using IOHIDManager to talk to it. The firmware for the device is written in C/C++, using GCC for ARM, and a library called TinyUSB. USB is officially USB-16LE, but it seems that's not well-enforced by anyone.

And just now it dawns on me that the TinyUSB library is not letting me set the Product String sequence to a UTF-16-encoded string, because it's setting the first \0 and stopping. So I have to get past that first.

1 Like

Okay it seems the TinyUSB API (the way it’s used in Arduino) takes UTF-8 strings and converts them internally to UTF-16 encoding. So my string u8"Wahangū" shows up in USB Prober as "Wahang\u016b", and gets read by IOHIDDeviceGetProperty() and converted to String:

	if let ps = IOHIDDeviceGetProperty(self.device, "Product" as CFString) as? String
	{
		debugLog("Product: \(ps)")
	}

And I get `"Wahangk" in Xcode’s console.

I’ll file a bug with Apple to see if it's IOKit.