Communication via HCE (NFC) on iOS¶
This guide explains how to enable HCE-based unlocking on iOS using the Tapkey Mobile SDK.
Before proceeding, make sure to review the HCE Overview.
Familiarize Yourself with Apple HCE APIs¶
This guide focuses on integrating the Tapkey Mobile SDK with iOS's native HCE APIs. Before starting implementation, it is strongly recommended to review Apple’s documentation on HCE, including entitlement setup and platform requirements: Apple HCE Documentation
European Economic Area only
Apple restricts the use of HCE functionality to devices operating within the European Economic Area (EEA). This feature is not available on devices registered outside of the EEA.
Info
The configuration steps provided below — particularly those related to Apple-specific setup — are best-effort guidance. It may be necessary to adapt parts of the integration based on the specific app configuration or future platform updates.
Refer to Apple’s official documentation for the most up-to-date requirements.
Prerequisites¶
The following conditions must be fulfilled before starting development:
- Minimum iOS Version: 17.4 or later
- AID (Application Identifier): A custom AID must be provisioned and maintained by the integrator.
- Entitlement: HCE entitlement must be explicitly requested from Apple.
- Locks: Compatible locks must be available and pre-configured to accept the specified AID.
Configure App Capabilities and Entitlements¶
Enable HCE in the App Entitlements¶
In the app’s entitlements file, the property com.apple.developer.nfc.hce
must be enabled.
Additionally, the correct AID must be registered under com.apple.developer.nfc.hce.iso7816.select-identifier-prefixes
.
<plist version="1.0">
<dict>
...
<key>com.apple.developer.nfc.hce</key>
<true/>
<key>com.apple.developer.nfc.hce.iso7816.select-identifier-prefixes</key>
<array>
<string>YOUR_AID</string>
</array>
</dict>
</plist>
Register as Default HCE Handler (Optional)¶
On iOS, only one application can handle HCE events in the background. By default, this is the Apple Wallet app. If multiple apps support HCE, the user may select a default app in device settings.
To allow the app to be eligible as the default HCE handler, add the following entitlement:
<plist version="1.0">
<dict>
...
<key>com.apple.developer.nfc.hce.default-contactless-app</key>
<true/>
</dict>
</plist>
Configure the Tapkey Mobile SDK¶
Set the Application Identifier (AID) during SDK initialization to enable HCE-based communication:
TapkeyMobileSdk.initialize { builder in
builder
.setAid(YOUR_AID)
....
}
Implement the CardSession Flow¶
iOS uses the CardSession API to manage HCE communication. Refer to Apple’s official CardSession Documentation for general implementation guidelines.
Passing the CardSession to the Tapkey SDK¶
After establishing a CardSession
and detecting an NFC reader, pass the session to the hceLockCommunicator
of the Tapkey Mobile SDK. The command to be executed must be provided as a callback, which will be invoked once a connection to a Tapkey-enabled lock is established.
// Check if CardSession is supported and available
guard NFCReaderSession.readingAvailable,
CardSession.isSupported,
await CardSession.isEligible else {
// Handle case where CardSession is not available on the device
return
}
// Retrieve HceLockCommunicator from the SDK
var hceLockCommunicator = TapkeyMobileSdk.serviceFactory.hceLockCommunicator
// Start a CardSession as described in Apple’s documentation
var cardSession = try await CardSession()
let asyncIterator: CardSession.EventStream.Iterator = cardSession.eventStream.makeAsyncIterator()
// Wait for session to start
guard case .sessionStarted = try await asyncIterator.next() else {
// Handle failure to start session
return
}
// Start emulation
try await cardSession.startEmulation()
// Wait until a reader is detected
guard case .readerDetected = try await asyncIterator.next() else {
// Handle missing reader
return
}
...
// Pass the card session to Tapkey SDK
// Note: No other consumer must access the event stream concurrently
var commandResult = try await hceLockCommunicator.executeCommandAsync(cardSession: cardSession, physicalLockId: nil) {
@MainActor (tlcpConnection, publicState) async throws in
return try await commandExecutionFacade.triggerLockAsync(tlcpConnection)
}
switch code {
case TKMCommandResult.TKMCommandResultCode.ok:
// Command executed successfully
break
// Handle additional result cases as needed
...
}
Reacting to HCE Events in the Background¶
The most seamless user experience is achieved when the device initiates NFC communication simply by being brought near the lock — without requiring the app to be manually opened.
However, on iOS, this behavior is subject to platform restrictions:
- Only one application on the device can handle HCE events when in the background.
- By default, this is the Apple Wallet app.
- Users have the option to select an alternative default application in the iOS Settings app.
- If an alternative app is selected, the Wallet app must then be opened manually for its NFC functionality to remain accessible.