EASession(accessory:forProtocol:) always returns nil — MFI accessory iAP2

EASession(accessory:forProtocol:) always returns nil — MFI accessory iAP2

Platform: iOS 17+ | Hardware: Custom MFI-certified accessory (USB-C, iAP2) | Language: Swift


Problem

We have a custom MFI-certified accessory communicating over USB-C using ExternalAccessory. The app calls EASession(accessory:forProtocol:) after receiving EAAccessoryDidConnect but it always returns nil. We never get past session creation.


What we have verified

We captured a sysdiagnose on-device and analysed the accessoryd-packets log. The full iAP2 handshake completes successfully at the OS level:

  • USB attach succeeds
  • MFI auth certificate is present and Apple-issued
  • Auth challenge and response complete successfully
  • IdentificationInformation is accepted by iOS — protocol string and Team ID are correct
  • EAAccessoryDidConnect fires as expected
  • iOS sends StartExternalAccessoryProtocolSession — the OS-level session is established

So the hardware, MFI auth, protocol string, and Team ID are all correct. Despite this, EASession(accessory:forProtocol:) returns nil in the app.

We also confirmed:

  • Protocol string in UISupportedExternalAccessoryProtocols in Info.plist matches the accessory exactly
  • Protocol string in code matches Info.plist
  • App entitlements are correctly configured
  • EAAccessoryManager.shared().registerForLocalNotifications() is called before connection

Current connection code

@objc private func accessoryDidConnect(_ notification: Notification) { guard let accessory = notification.userInfo?[EAAccessoryKey] as? EAAccessory else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { self.tryConnectToAccessory() } }

private func tryConnectToAccessory() { DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { for accessory in EAAccessoryManager.shared().connectedAccessories { let session = EASession(accessory: accessory, forProtocol: "<our-protocol-string>") // session is always nil here } } }


Questions

  1. The packet log shows a ~4 second gap between EAAccessoryDidConnect firing and iOS internally completing session readiness (StartExternalAccessoryProtocolSession). Is there a reliable way to know when iOS

Is it actually ready to grant an EASession, rather than using a fixed delay?

  1. Is there a delegate callback or notification that fires when the accessory protocol session is ready to be opened, rather than relying on EAAccessoryDidConnect + an arbitrary delay?

  2. Are there any known conditions on iOS 17+ under which EASession returns nil even though the iAP2 handshake completed successfully at the OS level?

  3. Is retrying EASession after a nil result a supported pattern, or does a nil result mean the session will never succeed for that connection?

Any guidance appreciated.

We have a custom MFI-certified accessory communicating over USB-C using ExternalAccessory. The app calls EASession(accessory:forProtocol:) after receiving EAAccessoryDidConnect, but it always returns nil. We never get past session creation.

A few questions:

  1. Ruling out an obvious issue, you're testing on a real iOS device, not the simulator, correct?

  2. Is this a SwiftUI or UIKit app?

  3. If this is a SwiftUI app, try integrating ExternalAccessory support into a basic UIKit project and then retest.

I'm not sure what the current situation is, but there are longstanding issues with the ExternalAccessory framework and SwiftUI, and #3 is the easiest way to rule out any other issues.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

EASession(accessory:forProtocol:) always returns nil — MFI accessory iAP2
 
 
Q