Version: v0.13
NFC Transport
NFC (Near Field Communication) provides a tap-and-go experience for mDoc presentations. This guide covers platform setup and usage patterns.
NFC in mDoc
NFC can be used for both engagement and data transfer:
| Scenario | Description |
|---|---|
| NFC Engagement | Holder taps device to initiate connection |
| NFC Data Transfer | Credential data exchanged via NFC |
| NFC to BLE Handover | Engage via NFC, transfer via BLE |
Checking NFC Availability
Check if NFC transport is available on the engagement:
- Android/Kotlin
- iOS/Swift
val engagement = engagementManager.activeEngagement.value
val retrievalMethods = engagement?.getRetrievalMethods() ?: emptySet()
for (method in retrievalMethods) {
if (method is DeviceRetrievalMethod.Nfc) {
println("NFC available")
}
}
let engagement = engagementManager.activeEngagement.value
let retrievalMethods = engagement?.getRetrievalMethods() ?? []
for method in retrievalMethods {
if method is DeviceRetrievalMethod.Nfc {
print("NFC available")
}
}
NFC Engagement Flow
Observe NFC engagement state:
- Android/Kotlin
- iOS/Swift
lifecycleScope.launch {
engagementManager.nfcEngagement.collect { engagement ->
if (engagement != null) {
// NFC tap detected, engagement created
println("NFC engagement: ${engagement.id}")
engagement.events.collect { event ->
when (event.state) {
is MdocEngagementState.Connected -> {
// Start transfer
val transferManager = engagement.start()
handleTransfer(transferManager)
}
}
}
}
}
}
Task {
for await engagement in engagementManager.nfcEngagement {
guard let engagement = engagement else { continue }
// NFC tap detected, engagement created
print("NFC engagement: \(engagement.id)")
for await event in engagement.events {
switch event.state {
case .connected:
// Start transfer
let transferManager = try await engagement.start()
try await handleTransfer(transferManager: transferManager)
default:
break
}
}
}
}
Android Setup
Host Card Emulation (HCE)
Android uses Host Card Emulation to present credentials via NFC.
Manifest Configuration
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<uses-feature android:name="android.hardware.nfc.hce" android:required="false" />
<application>
<!-- HCE Service for mDoc -->
<service
android:name=".MdocNfcService"
android:exported="true"
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
</intent-filter>
<meta-data
android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/mdoc_nfc_service" />
</service>
</application>
Service Configuration
Create res/xml/mdoc_nfc_service.xml:
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/nfc_service_description"
android:requireDeviceUnlock="true">
<aid-group
android:category="other"
android:description="@string/nfc_aid_description">
<!-- mDoc AID per ISO 18013-5 -->
<aid-filter android:name="A0000002480400" />
</aid-group>
</host-apdu-service>
iOS Setup
Info.plist Configuration
<key>NFCReaderUsageDescription</key>
<string>Present your credentials via NFC tap</string>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<!-- mDoc AID -->
<string>A0000002480400</string>
</array>
Entitlements
Add the NFC Tag Reading capability to your app's entitlements.
NFC Protocol Flow
Holder Device Verifier Reader
│ │
│ 1. Tap device on reader │
│────────────────────────────────────►│
│ │
│ 2. SELECT command (AID) │
│◄────────────────────────────────────│
│ │
│ 3. SELECT response │
│────────────────────────────────────►│
│ │
│ 4. APDU commands (request/response)│
│◄───────────────────────────────────►│
│ │
│ 5. Complete │
│────────────────────────────────────►│
Monitoring NFC State
Use the event hub to monitor NFC status:
- Android/Kotlin
- iOS/Swift
engagementManager.eventHub.engagementEvents.collect { event ->
when (event.state) {
is MdocEngagementState.Connecting -> {
showMessage("NFC connection in progress...")
}
is MdocEngagementState.Connected -> {
showMessage("Connected via NFC")
}
is MdocEngagementState.Transferring -> {
showMessage("Transferring data via NFC...")
}
is MdocEngagementState.Error -> {
showMessage("NFC error occurred")
}
}
}
for await event in engagementManager.eventHub.engagementEvents {
switch event.state {
case .connecting:
showMessage(text: "NFC connection in progress...")
case .connected:
showMessage(text: "Connected via NFC")
case .transferring:
showMessage(text: "Transferring data via NFC...")
case .error:
showMessage(text: "NFC error occurred")
default:
break
}
}
NFC to BLE Handover
NFC engagement with BLE data transfer provides the best of both worlds:
- Quick NFC tap for engagement (no QR scanning)
- Higher bandwidth BLE for actual data transfer
- Android/Kotlin
- iOS/Swift
// Observe NFC engagement
lifecycleScope.launch {
engagementManager.nfcEngagement.collect { engagement ->
if (engagement != null) {
// Check available retrieval methods
val methods = engagement.getRetrievalMethods()
// If BLE is available, it will be used for transfer
if (methods.any { it is DeviceRetrievalMethod.Ble }) {
println("NFC engagement with BLE transfer")
}
}
}
}
Task {
for await engagement in engagementManager.nfcEngagement {
guard let engagement = engagement else { continue }
// Check available retrieval methods
let methods = engagement.getRetrievalMethods()
// If BLE is available, it will be used for transfer
if methods.contains(where: { $0 is DeviceRetrievalMethod.Ble }) {
print("NFC engagement with BLE transfer")
}
}
}
Large Data Considerations
NFC has limited bandwidth compared to BLE. For credentials with photos:
- Transfer may take several seconds
- Keep devices in contact until transfer completes
- Consider NFC-to-BLE handover for large payloads
- Provide visual feedback during transfer
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| NFC not detected | Device not NFC-enabled | Check device capabilities |
| Tap not recognized | AID not registered | Verify manifest configuration |
| Transfer interrupted | Tap released too early | Keep devices in contact |
| Permission denied | Missing permissions | Add NFC permission to manifest |
| HCE not working | Background restrictions | Ensure app is in foreground |
Best Practices
When implementing NFC transport:
- Keep devices in contact: NFC disconnects as soon as devices separate
- Provide visual feedback: Show progress so users know to maintain the tap
- Handle background case: NFC HCE may not work when app is in background
- Test with real hardware: NFC behavior varies between manufacturers
- Consider handover: Use NFC engagement with BLE transfer for large credentials