@vppos/react-native-nitro-nfc
v2.0.0
Published
React Native Nitro NFC reader for Vietnamese citizen ID chips
Readme
@vppos/react-native-nitro-nfc
React Native Nitro Module for reading Vietnamese citizen ID cards through NFC. It provides the native scan UI, PACE authentication, chip data parsing, progress events, and short-lived native caching. The npm package includes its Android AAR and iOS XCFrameworks, so an app does not need a separate NFC-core Maven or CocoaPods repository.
Requirements
- React Native with the New Architecture enabled and
react-native-nitro-modules. - Android API 24+ on a physical device with NFC enabled.
- iOS 15+ on a physical device. Enable the Near Field Communication Tag Reading capability for the app's Apple Developer profile.
Android emulators and iOS Simulators cannot read an NFC chip.
Install
npm install @vppos/react-native-nitro-nfc react-native-nitro-modules
# iOS
cd ios && pod install && cd ..Rebuild the native app after installation; reloading Metro is not enough.
npx react-native run-android
npx react-native run-ios --deviceReact Native and Nitro autolinking register the module automatically.
Native setup
Android
The library contributes NFC and vibration permissions through manifest merging.
The host app must use minSdk 24 or higher:
android {
defaultConfig {
minSdk 24
}
}Do not add a :nfc-core Gradle project, a private NFC Maven repository, or NFC
SDK credentials. The bundled nfc-core.aar is used automatically.
iOS
Configure both of the following in the host application target. They are
different settings: Info.plist supplies the user-facing system message, while
the entitlement authorizes NFC Tag Reading in the signed app.
- Add the NFC usage description and the ISO 7816 application identifiers to
ios/<App>/Info.plist:
<key>NFCReaderUsageDescription</key>
<string>Ứng dụng sử dụng NFC để đọc dữ liệu từ thẻ căn cước công dân gắn chip.</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
<string>A0000002472001</string>
<string>00000000000000</string>
</array>Add these keys to the actual host app target's Info.plist, not only to a
test target. Add only application identifiers approved for the card flows
your app supports.
- In Xcode, select the app target, open Signing & Capabilities, and add
Near Field Communication Tag Reading. Verify that the app's
*.entitlementsfile contains at least:
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>The entitlement must also be enabled in the Apple Developer provisioning profile
used to sign the app. It is not enough to add only NFCReaderUsageDescription.
The ISO 7816 identifiers above must be permitted by that profile.
After changing either setting, run cd ios && pod install and rebuild the app
on a physical iPhone.
Scan a card
The public NFCError class is the only error contract exposed by the package.
It is used both in progress events and Promise rejections, with stable code
and localized message fields.
import { NFCError, NFCSDK } from '@vppos/react-native-nitro-nfc';
export async function readCitizenCard(citizenId: string) {
if (!NFCSDK.isAvailable()) {
throw new Error('NFC is unavailable on this device.');
}
try {
return await NFCSDK.scan({
citizenId,
readImage: true,
cachePolicy: 'fresh',
language: 'vi',
onProgress(event) {
if ('error' in event) {
// Store this object directly in host UI state.
const error: NFCError = event.error;
console.warn(error.code, error.message);
return;
}
// Update UI with event.progress, event.phase, and event.message.
},
});
} catch (error) {
if (error instanceof NFCError) {
// The error has the same code/message shape as event.error.
throw error;
}
throw error;
}
}citizenId must contain digits only and be at least six characters. A
Vietnamese citizen ID normally has 12 digits; the core uses its last six digits
as the CAN key.
scan() resolves to lightweight metadata (NFCScanResult). It includes the
citizen details, a cached image URI when requested, and the sizes of available
data groups. Set readImage: false to skip DG2/portrait reading when it is not
needed.
Progress and errors
Normal progress events expose progress (0–100), message, and a stable
phase: opening, waiting-for-tag, connecting, authenticating, reading,
or success.
Errors are always exposed as { code, message }. The code is stable and is not
localized; the message follows the requested language. Treat UserCanceled as
a normal cancellation rather than a system failure.
scan() rejects with NFCError, whose code matches the stable native error
code supplied in progress events. The rejection exposes the same code and
message, so the host can store either value directly in one error state
without parsing native error strings.
Use either the onProgress callback passed to scan() or the global
NFCSDK.onProgress() listener for one UI state, not both, to avoid duplicate
error handling.
Raw data and cache
Raw data groups are loaded only on demand. Prefer ArrayBuffer rather than
Base64 to reduce memory use:
const dg1 = NFCSDK.getDataGroupBuffer('DG1');
const image = NFCSDK.getDataGroupBuffer('IMAGE');
// Clear metadata, chip image, and short-lived native cache when finished.
NFCSDK.clearCachedScan();Supported group names are IMAGE, DG1, DG2, DG13, DG14, and SOD.
With cachePolicy: 'reuse-if-valid', only DG2 can be reused when the DG1/SOD
fingerprint matches; DG1 and SOD are still read from the chip.
Do not write raw DG bytes, portrait images, CAN, or citizen ID data to logs or analytics unless your data-handling policy explicitly permits it.
iOS OpenSSL mode
By default, the library uses a self-contained dynamic NFCCore.xcframework
with private OpenSSL. This is the recommended mode.
If the host must supply its own compatible Swift module named OpenSSL, select
the static host-OpenSSL variant before installing pods:
NITRO_NFC_USE_MANUAL_OPENSSL=1 pod installThe host is responsible for linking exactly one compatible OpenSSL provider in
that mode. NFCSDK_USE_MANUAL_OPENSSL=1 remains supported as a legacy alias.
API
NFCSDK.isAvailable(): booleanNFCSDK.scan(options): Promise<NFCScanResult>NFCSDK.startScan(options): voidNFCSDK.onProgress(listener): NFCSubscriptionNFCSDK.onScanResult(listener): NFCSubscriptionNFCSDK.getDataGroupBuffer(name): ArrayBuffer | undefinedNFCSDK.getDataGroupBase64(name): string | undefinedNFCSDK.clearCachedScan(): void
See the repository's integration guide for the complete error and production test matrix.
License
MIT
