From 5680c75921cf96d63b36fbb6cb29edeee99a2287 Mon Sep 17 00:00:00 2001 From: darekaze <32747549+darekaze@users.noreply.github.com> Date: Tue, 30 Jun 2026 22:12:30 +0900 Subject: [PATCH 1/3] Refresh README for open-source launch. Clarify installation, native build requirements, unsupported targets, Reader flow, and manual hardware validation without overstating repo automation. Co-authored-by: Cursor --- README.md | 169 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 96 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 67b296b..02ff0bb 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,18 @@ # react-native-ble-nfc-reader -Expo native module for ACS BLE NFC Readers. +Expo native module for ACS BLE NFC Readers — scan for a Reader, connect over Bluetooth, read card UIDs, send raw APDUs, and work with MIFARE Classic Cards. + +> [!IMPORTANT] +> This package ships native Android and iOS code. **Expo Go and web are not supported.** Install it into an Expo app with a [development build](https://docs.expo.dev/develop/development-builds/introduction/) or a bare React Native app, then run `expo prebuild` / `npx expo run:android` or `npx expo run:ios` so the native module is compiled in. ## Installation -Install the package from npm: +Install from [npmjs.com](https://www.npmjs.com/package/react-native-ble-nfc-reader): ```sh npm install react-native-ble-nfc-reader ``` -npm is the primary install path for native React Native and Expo apps. JSR is -secondary and, if used, is only for TypeScript API, types, and docs. - -## Requirements - -- Android 6.0+ (API 23+) -- iOS 15.0+ -- Native development build or prebuilt app; Expo Go and web are not supported. -- ACS BLE Reader supported by the bundled ACS SDK binaries. -- MIFARE Classic Card for the MIFARE Classic helpers. - -This package contains native Android and iOS code. After installing it, build a -native development build or run prebuild before using the module. - Add the config plugin to your app config: ```json @@ -34,82 +23,116 @@ Add the config plugin to your app config: } ``` -The plugin adds Android BLE permissions and iOS Bluetooth usage descriptions. +The plugin adds Android BLE permissions and iOS Bluetooth usage descriptions. Rebuild the native app after adding or updating the plugin. + +## Requirements + +| | | +| --- | --- | +| **Platforms** | Android 6.0+ (API 23+), iOS 15.0+ | +| **App type** | Expo development build or bare React Native — not Expo Go, not web | +| **Hardware** | ACS BLE Reader supported by the bundled ACS SDK binaries | +| **Cards** | MIFARE Classic Card for the `mifare` helpers | + +See the [`example/`](example/) app for a minimal Expo development-build setup. + +## Reader flow + +Typical integration order: + +1. **Permissions** — check and request Reader permission before scanning. +2. **Scan** — discover nearby Readers for a bounded window. +3. **Connect** — connect one Reader at a time. +4. **Card** — listen for card presence, read UIDs, transmit APDUs, or use MIFARE Classic helpers. + +### Reader permissions -## Reader permissions +Call `getReaderPermissionStatus()` before scanning. Call `requestReaderPermissions()` when the app needs to prompt. -Use `getReaderPermissionStatus()` before scanning and `requestReaderPermissions()` -when the app needs to prompt. Android reports missing runtime Bluetooth/location -permission as `denied` even before the first request; call -`requestReaderPermissions()` to ask for access. On iOS, calling `scanReaders()` -before Bluetooth permission has been requested rejects with -`READER_PERMISSION_UNDETERMINED`; check status and request Reader permission -before scanning. `READER_PERMISSION_DENIED` means runtime access was denied, -while `READER_PERMISSION_MISSING` means the native permission declaration is -missing. +| Status | Meaning | +| --- | --- | +| `READER_PERMISSION_UNDETERMINED` | Not asked yet — request before scanning (iOS rejects `scanReaders()` until permission is requested) | +| `READER_PERMISSION_DENIED` | User denied runtime access | +| `READER_PERMISSION_MISSING` | Native permission declaration is missing from the app | -## Reader scanning +On Android, missing Bluetooth/location permission may report as `denied` before the first request; call `requestReaderPermissions()` to prompt. -Use `scanReaders({ timeoutMs })` for bounded scans and -`addReaderDiscoveredListener()` to receive Readers during the scan window. Call -`stopReaderScan()` to end the active scan early. Starting a new `scanReaders()` -while one is active supersedes the prior bounded scan: the prior promise resolves -with partial Reader results collected so far, and discovery events for that scan -stop once it ends. +### Reader scanning -## Reader connection +- `scanReaders({ timeoutMs })` — bounded scan; resolves with discovered Readers. +- `addReaderDiscoveredListener()` — receive Readers during the scan window. +- `stopReaderScan()` — end the active scan early. -Use `connectReader(readerId)` with a discovered Reader ID to connect one Reader -per app process. It returns the connected `Reader`; `metadata` may include model, -firmware version, serial number, or battery level when the ACS Reader provides -those fields. Call `disconnectReader(readerId)` to release the native Reader -connection before connecting another Reader. If `disconnectReader()` rejects, -treat the Reader as still active and retry disconnect before connecting another -Reader. +Starting a new `scanReaders()` while one is active supersedes the prior scan: the prior promise resolves with partial results collected so far, and discovery events for that scan stop once it ends. -## Card and APDU +### Reader connection -Use `addCardPresentListener()` and `addCardRemovedListener()` after connecting a -Reader. `readCardUid(readerId)` returns the presented card UID as a Hex String. -`transmit(readerId, apdu)` sends a raw APDU Hex String and resolves with -`responseData` and `status`; non-`9000` APDU statuses are returned, not thrown. +- `connectReader(readerId)` — connect one Reader per app process; returns the connected `Reader`. `metadata` may include model, firmware version, serial number, or battery level when the ACS Reader provides those fields. +- `disconnectReader(readerId)` — release the native connection before connecting another Reader. If disconnect rejects, treat the Reader as still active and retry before connecting elsewhere. -## MIFARE Classic +### Card and APDU -Use `mifare.authenticateBlock({ readerId, block, keyType, key })` with a -caller-owned key before `mifare.readBlock({ readerId, block })` or -`mifare.writeBlock({ readerId, block, data })`. Keys are loaded into the Reader -only for the operation and are not stored by this package. `readBlock()` returns -16 bytes as a Hex String. `writeBlock()` rejects trailer blocks unless -`allowTrailerWrite` is set. +After connecting a Reader: + +- `addCardPresentListener()` / `addCardRemovedListener()` — card presence events. +- `readCardUid(readerId)` — card UID as a Hex String. +- `transmit(readerId, apdu)` — send a raw APDU Hex String; resolves with `responseData` (APDU Response Data) and `status` (APDU Status). Non-`9000` statuses are returned, not thrown. + +### MIFARE Classic + +Use `mifare.authenticateBlock({ readerId, block, keyType, key })` with a caller-owned key before `mifare.readBlock({ readerId, block })` or `mifare.writeBlock({ readerId, block, data })`. + +- Keys are loaded into the Reader only for the operation and are not stored by this package. +- `readBlock()` returns 16 bytes as a Hex String. +- `writeBlock()` rejects trailer blocks unless `allowTrailerWrite` is set. ## Manual hardware checklist -- Android: grant Reader permission, start a 5 second scan, confirm an ACS BLE - Reader appears during the scan, then confirm scanning stops at timeout and - after `stopReaderScan()`. Connect the discovered Reader, confirm optional - metadata appears when available, present and remove a card, confirm the card - presence events update, read the card UID, transmit `FFCA000000`, confirm APDU - Response Data and APDU Status are shown separately, authenticate a MIFARE - Classic block with a caller-owned key, read the block, write 16 bytes, read it - back, disconnect it, then confirm a second connect works only after disconnect. -- iOS: grant Bluetooth permission, start a 5 second scan, confirm an ACS BLE - Reader appears during the scan, then confirm scanning stops at timeout and - after `stopReaderScan()`. Connect the discovered Reader, confirm optional - metadata appears when available, present and remove a card, confirm the card - presence events update, read the card UID, transmit `FFCA000000`, confirm APDU - Response Data and APDU Status are shown separately, authenticate a MIFARE - Classic block with a caller-owned key, read the block, write 16 bytes, read it - back, disconnect it, then confirm a second connect works only after disconnect. +Run these checks on real hardware when changing Reader, card, or MIFARE behavior. PRs that touch those flows should note what was tested. + +**Android** + +1. Grant Reader permission. +2. Start a 5 second scan; confirm an ACS BLE Reader appears. +3. Confirm scanning stops at timeout and after `stopReaderScan()`. +4. Connect the discovered Reader; confirm optional metadata when available. +5. Present and remove a card; confirm presence events update. +6. Read the card UID. +7. Transmit `FFCA000000`; confirm APDU Response Data and APDU Status are shown separately. +8. Authenticate a MIFARE Classic block with a caller-owned key, read the block, write 16 bytes, read it back. +9. Disconnect; confirm a second connect works only after disconnect. + +**iOS** + +1. Grant Bluetooth permission. +2. Start a 5 second scan; confirm an ACS BLE Reader appears. +3. Confirm scanning stops at timeout and after `stopReaderScan()`. +4. Connect the discovered Reader; confirm optional metadata when available. +5. Present and remove a card; confirm presence events update. +6. Read the card UID. +7. Transmit `FFCA000000`; confirm APDU Response Data and APDU Status are shown separately. +8. Authenticate a MIFARE Classic block with a caller-owned key, read the block, write 16 bytes, read it back. +9. Disconnect; confirm a second connect works only after disconnect. ## ACS SDK -ACS SDK binaries are bundled in the native package setup: +ACS SDK binaries are bundled in the native package: - `android/libs/acssmcio-*.aar` - `android/libs/smartcardio-*.aar` - `ios/Frameworks/ACSSmartCardIO.xcframework` - `ios/Frameworks/SmartCardIO.xcframework` -See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for bundled ACS and -SmartCardIO/OpenJDK notices. +See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for bundled ACS and SmartCardIO/OpenJDK notices. + +## Development + +From the repo root: + +```sh +pnpm install +pnpm run lint +pnpm run build +``` + +Changes to Reader or card flows need the manual hardware checklist on physical devices. From 32c94a6d6c2d66cdca47b1d2333039c9270144f8 Mon Sep 17 00:00:00 2001 From: darekaze <32747549+darekaze@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:27:54 +0900 Subject: [PATCH 2/3] Add npm version badge to README. Link the shields.io badge to the published @countertek package on npmjs.com. Co-authored-by: Cursor --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9049bac..a658803 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # react-native-ble-nfc-reader +[![npm version](https://img.shields.io/npm/v/@countertek/react-native-ble-nfc-reader)](https://www.npmjs.com/package/@countertek/react-native-ble-nfc-reader) + Expo native module for ACS BLE NFC Readers — scan for a Reader, connect over Bluetooth, read card UIDs, send raw APDUs, and work with MIFARE Classic Cards. > [!IMPORTANT] From 6fa35b2bf470340c7d0a34ed69e53de5845c67f1 Mon Sep 17 00:00:00 2001 From: darekaze <32747549+darekaze@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:32:03 +0900 Subject: [PATCH 3/3] Clean up duplicate README sections after merge. Remove the extra Requirements block and repeated native-build note left from merging main into fix/issue-35; keep one Installation + Requirements flow. Co-authored-by: Cursor --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index a658803..6257d43 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,6 @@ npm install @countertek/react-native-ble-nfc-reader npm is the public distribution path for this native module. See [docs/release.md](docs/release.md) for maintainer release steps. -## Requirements - -- Android 6.0+ (API 23+) -- iOS 15.0+ -- Native development build or prebuilt app; Expo Go and web are not supported. -- ACS BLE Reader supported by the bundled ACS SDK binaries. -- MIFARE Classic Card for the MIFARE Classic helpers. - -This package contains native Android and iOS code. After installing it, build a -native development build or run prebuild before using the module. - Add the config plugin to your app config: ```json