DriverKit issue with TestFlight

Hi,

We’re developing a DriverKit extension for iPadOS. In local Debug and Release builds, everything works as expected, but the same build uploaded to TestFlight fails at IOServiceOpen with the following errors:

-536870212 (0xE00002EC) kIOReturnUnsupported

-536870201 (0xE00002F7) kIOReturnNotPermitted

What we’ve verified so far

  1. App entitlements

We checked our main app entitlements file, and it has the correct capabilities for the driverkit communication

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.driverkit.communicates-with-drivers</key>
	<true/>
	<key>com.apple.developer.driverkit.userclient-access</key>
	<array>
		<string>abc.def.ABCDriver</string>
	</array>
	<key>com.apple.developer.system-extension.install</key>
	<true/>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.device.usb</key>
	<true/>
	<key>com.apple.security.files.user-selected.read-write</key>
	<true/>
</dict>
</plist>
  1. we also checked the Provisioning profile (as shown on the portal) and the “Enabled Capabilities” seems to have the correct DriverKit Capabilities enabled.
Enabled Capabilities

Access Wi-Fi Information, DriverKit, DriverKit (development), DriverKit Communicates with Drivers, DriverKit USB Transport (development), DriverKit USB Transport - VendorID, DriverKit UserClient Access, iCloud, In-App Purchase, Sign In with Apple, System Extension
  1. When we download and inspect the provisioning profile as plain text, we notice that some expected DriverKit entitlements appear to be missing from the <Entitlements> section.
<key>Entitlements</key>
<dict>
    <key>beta-reports-active</key>
    <true/>
          
          <key>com.apple.developer.networking.wifi-info</key>
    <true/>
          
          <key>com.apple.developer.driverkit</key>
    <true/>
          
          <key>com.apple.developer.driverkit.communicates-with-drivers</key>
    <true/>
          
          <key>application-identifier</key>
    <string>ABC123456.abc.def</string>
          
          <key>keychain-access-groups</key>
    <array>
          <string>ABC123456.*</string>
          <string>com.apple.token</string>
    </array>
          
          <key>get-task-allow</key>
    <false/>
          
          <key>com.apple.developer.team-identifier</key>
    <string>ABC123456</string>
          
          <key>com.apple.developer.ubiquity-kvstore-identifier</key>
    <string>ABC123456.*</string>
          
          <key>com.apple.developer.icloud-services</key>
    <string>*</string>
          
          <key>com.apple.developer.icloud-container-identifiers</key>
    <array></array>
          
          <key>com.apple.developer.icloud-container-development-container-identifiers</key>
    <array></array>
          
          <key>com.apple.developer.ubiquity-container-identifiers</key>
    <array></array>
          
          <key>com.apple.developer.driverkit.transport.usb</key>
    <array>
                <dict>
          
          <key>idVendor</key>
    <integer>1234</integer>
                </dict>
    </array>
          
          <key>com.apple.developer.applesignin</key>
    <array>
          <string>Default</string>
    </array>

</dict>

We have a couple of questions:

  1. Could the missing com.apple.developer.driverkit.userclient-access entitlement in the provisioning profile alone explain the kIOReturnUnsupported / kIOReturnNotPermitted failures from IOServiceOpen?

  2. Why do some DriverKit capabilities appear in the Apple Developer portal UI but vanish from the actual profile we download? Is there an extra step we’re overlooking when regenerating profiles after toggling those capabilities?

Thanks

In addition to the main app's entitlements and provisioning profile described above, here are the entitlements and provisioning profile details for our DriverKit extension:

Driver Entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.driverkit</key>
	<true/>
	<key>com.apple.developer.driverkit.transport.usb</key>
	<array>
		<dict/>
	</array>
	<key>com.apple.security.app-sandbox</key>
	<true/>
</dict>
</plist>

Entitlements section in the Driver's Provisioning Profile


    <key>Entitlements</key>
    <dict>
        <key>beta-reports-active</key>
        <true/>
                
                <key>com.apple.developer.driverkit</key>
        <true/>
                
                <key>application-identifier</key>
        <string>ABC123456.abc.def.ABCDriver</string>
                
                <key>com.apple.developer.driverkit.transport.usb</key>
        <array>
                        <dict>
                
                <key>idVendor</key>
        <integer>1234</integer>
                        </dict>
        </array>
                
                <key>com.apple.developer.driverkit.allow-third-party-userclients</key>
        <true/>
                
                <key>keychain-access-groups</key>
        <array>
                <string>ABC123456.*</string>
                <string>com.apple.token</string>
        </array>
                
                <key>get-task-allow</key>
        <false/>
                
                <key>com.apple.developer.team-identifier</key>
        <string>ABC123456</string>

    </dict>

Please ignore the IOServiceOpen errors above.

We've observed that after uploading our build to TestFlight, the driver is no longer being detected. Specifically, we’re unable to find the matching driver service, and the expected "Service name: ..." log does not appear—even after connecting the hardware and enabling the driver in the iOS App settings.

The same code works reliably in local (non-TestFlight) builds, where we're able to successfully detect the driver and establish a connection. Here's the relevant snippet for reference:

func isDriverMatched() -> Bool {
    var bDriverMatched = false
    var serviceMatchItr: io_iterator_t = 0
    
    if #available(iOS 15.0, *) {
        guard let matchingDict = IOServiceMatching("IOUserService") else {
            Log.e(with: "TAG", with: "Failed to create matching dictionary")
            return false
        }
        
        let key = "IOUserServerName" as CFString
        let serverName = "abc.def.ABCDriver" as CFString
        CFDictionarySetValue(matchingDict, Unmanaged.passUnretained(key).toOpaque(), Unmanaged.passUnretained(serverName).toOpaque())
        
        let serviceMatchedError = IOServiceGetMatchingServices(kIOMainPortDefault, matchingDict, &serviceMatchItr)
        guard serviceMatchedError == kIOReturnSuccess else {
            PCOPiLog.e(with: "TAG", with: "Failed to find matching service")
            IOObjectRelease(serviceMatchItr)
            return false
        }
        
        var service: io_object_t
        repeat {
            service = IOIteratorNext(serviceMatchItr)
            if service != 0 {
                
                var serviceNameCString = [CChar](repeating: 0, count: 128)
                var serviceName = "Service name: <Unknown>"
                if IORegistryEntryGetName(service, &serviceNameCString) == KERN_SUCCESS {
                    serviceName = String(cString: serviceNameCString)
                    PCOPiLog.i(with: "TAG", with: "Service name: \(serviceName)")
                } else {
                    PCOPiLog.i(with: "TAG", with: "Service name: <Unknown>")
                }
                //
                ...........................                    

            }
        } while service != 0
        
        if !bDriverMatched {
            PCOPiLog.e(with: "TAG", with: "No matching driver service found or not responsive.")
        }
    } else {
        return false
    }
    
    IOObjectRelease(serviceMatchItr)
    return bDriverMatched
}

We investigated further by unzipping both the TestFlight .ipa and the archive from Xcode. The dext is present in both builds, but the .dext inside the .ipa is noticeably smaller (~150 KB) compared to the one in the archive (~350 KB).

We also extracted the entitlements from the signed app bundle in the .ipa. Here's what was included:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>application-identifier</key>
    <string>ABC123456.abc.def</string>
    <key>beta-reports-active</key>
    <true/>
    <key>com.apple.developer.driverkit.communicates-with-drivers</key>
    <true/>
    <key>com.apple.developer.team-identifier</key>
    <string>ABC123456</string>
    <key>get-task-allow</key>
    <false/>
  </dict>
</plist>

This set is missing some key entitlements compared to our app's original entitlements file. It’s unclear why the packaging or signing process is dropping these during TestFlight submission.

We also checked the entitlements embedded in the .dext itself (inside the .ipa)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>application-identifier</key>
    <string>ABC123456.abc.def.ABCDriver</string>
    <key>beta-reports-active</key>
    <true/>
    <key>com.apple.developer.driverkit</key>
    <true/>
    <key>com.apple.developer.driverkit.transport.usb</key>
    <array>
      <dict/>
    </array>
    <key>com.apple.developer.team-identifier</key>
    <string>ABC123456</string>
    <key>get-task-allow</key>
    <false/>
  </dict>
</plist>

Question:

Could the missing entitlements in the main app bundle from the .ipa—prevent the system from detecting or loading the driver at runtime? Also, could the smaller .dext size in the .ipa suggest it is being stripped or excluded in some way?

We would appreciate any insights or recommendations.

Thanks!

DriverKit issue with TestFlight
 
 
Q