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

Hi Kevin,

Thank you for the troubleshooting steps. To clarify the current state of the environment:

  1. Testing Environment: I am testing exclusively on a physical iOS device (iPhone [Model] running iOS [Version]); I am not using the simulator for these tests.

  2. Framework: The implementation is already built using UIKit. I am managing the accessory lifecycle within a standard UIViewController and AppDelegate structure.

  3. Integration Status: Despite being in a pure UIKit environment, I am still facing the issue where [mention the specific symptom, e.g., the accessory is not appearing in the picker / the session fails to open].

Since I have already ruled out SwiftUI lifecycle interference and simulator limitations, are there specific logging categories in Console.app or internal ExternalAccessory states you recommend I monitor to diagnose why the connection is failing?

Since I have already ruled out SwiftUI lifecycle interference and simulator limitations, are there specific logging categories in Console.app or internal ExternalAccessory states you recommend I monitor to diagnose why the connection is failing?

There isn't a lot that can go wrong from the app side, assuming your app is properly configured and "functional". As one more confirmation test, I would suggest testing with the old "EADemo" sample. The code is fairly old, but you should still be able to get it working without too much effort. If that's failing, then that's pretty strong evidence that it's either a bug in the system or a problem with the accessory.

Has your accessory passed certification? And have you "ever" been able to work with the ExternalAccessory framework/app?

Finally, have you filed a bug on this and, if so, what's the bug number?

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hi,

Thank you for the suggestions. I have tested the communication using the EADemo sample app as suggested, but unfortunately, I had no luck. The app is unable to establish a stable session with the accessory, which mirrors the issues we are seeing with our own application.

To move this forward, I have filed an official bug report via Feedback Assistant and attached a full sysdiagnose captured during a failed communication attempt:

Feedback ID: FB22116486

Current Project Status:

Certification: The accessory is currently in an early development state, so we have not opted for MFi certification yet. We are using development identifiers/chips for testing the iAP2 protocol.

Prior Success: We have not yet been able to successfully maintain a functional data session between the ExternalAccessory framework and this specific hardware revision.

Technical Observations from Firmware Logs: Based on our local logs, the hardware side appears to be performing correctly:

Link Synchronization: The iAP2 link completes the handshake successfully (Link params: 4096 65535) and reaches the iAP2LinkConnectedCB state.

Authentication: The MFi authentication challenge/response is successful (Auth passed).

Identification: The device identifies correctly as "ECG One" (Tricog Health India Private Limited), and the identification request is accepted by iOS.

Data Session: We see the start of an EA session (ID 30) and the firmware begins transmitting data (ASCII "hello\n"). While the iAP2 link layer is receiving ACKs from iOS at the transport level, the data never surfaces in the app layer (either in EADemo or our custom app).

Since a functional, authenticated link is failing to communicate even with the standard EADemo app, I am hoping the logs in FB22116486 will help identify if there is a protocol violation in our identification sequence or a configuration issue in our project's UISupportedExternalAccessoryProtocols.

Any further insights into why the session would "black-hole" data despite a successful handshake would be greatly appreciated. FB22116486

Certification: The accessory is currently in an early development state, so we have not opted for MFi certification yet. We are using development identifiers/chips for testing the iAP2 protocol.

OK. Since the accessory is in early development and uncertified, I'd recommend going through the MFi support process, the details of which are in the Developer Technical Support section of the MFi Licensing Handbook, which you can find in the MFi portal.

They can discuss all of the hardware details, as well as work with out engineering team(s) if there is an issue on our end.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

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