Skip to main content
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:

ScenarioDescription
NFC EngagementHolder taps device to initiate connection
NFC Data TransferCredential data exchanged via NFC
NFC to BLE HandoverEngage via NFC, transfer via BLE

Checking NFC Availability

Check if NFC transport is available on the engagement:

val engagement = engagementManager.activeEngagement.value
val retrievalMethods = engagement?.getRetrievalMethods() ?: emptySet()

for (method in retrievalMethods) {
if (method is DeviceRetrievalMethod.Nfc) {
println("NFC available")
}
}

NFC Engagement Flow

Observe NFC engagement state:

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)
}
}
}
}
}
}

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:

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")
}
}
}

NFC to BLE Handover

NFC engagement with BLE data transfer provides the best of both worlds:

  1. Quick NFC tap for engagement (no QR scanning)
  2. Higher bandwidth BLE for actual data transfer
// 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")
}
}
}
}

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

IssueCauseSolution
NFC not detectedDevice not NFC-enabledCheck device capabilities
Tap not recognizedAID not registeredVerify manifest configuration
Transfer interruptedTap released too earlyKeep devices in contact
Permission deniedMissing permissionsAdd NFC permission to manifest
HCE not workingBackground restrictionsEnsure 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