├── src ├── types │ ├── NdefTypes.ts │ └── HceTypes.ts ├── index.ts └── classes │ ├── HceTools.ts │ └── NdefTools.ts ├── tsconfig.json ├── package.json ├── .gitignore ├── index.d.ts ├── README.md └── LICENSE /src/types/NdefTypes.ts: -------------------------------------------------------------------------------- 1 | export type NdefTagInfo = { 2 | id: string; 3 | content: string; 4 | }; 5 | -------------------------------------------------------------------------------- /src/types/HceTypes.ts: -------------------------------------------------------------------------------- 1 | export type HceOptions = { 2 | content: string; 3 | writable: boolean; 4 | }; 5 | 6 | export type HceReadHandler = () => void; 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import NdefTools from "./classes/NdefTools"; 2 | import HceTools from "./classes/HceTools"; 3 | 4 | module.exports = { NdefTools, HceTools }; 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "rootDir": "./src", 5 | "outDir": "./dist", 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "skipLibCheck": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-nfc-sdk", 3 | "version": "0.3.4beta", 4 | "description": "A package which pretends to make NFC and HCE operations accessible to everyone on react native. Based on react-native-nfc-manager and react-native-hce", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/loridev/react-native-nfc-sdk.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "android", 16 | "ios", 17 | "nfc", 18 | "hce", 19 | "sdk" 20 | ], 21 | "author": "loridev", 22 | "license": "SEE LICENSE IN LICENSE", 23 | "bugs": { 24 | "url": "https://github.com/loridev/react-native-nfc-sdk/issues" 25 | }, 26 | "homepage": "https://github.com/loridev/react-native-nfc-sdk#readme", 27 | "dependencies": { 28 | "react-native-hce": "^0.2.0", 29 | "react-native-nfc-manager": "^3.14.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/classes/HceTools.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HCESessionEventListenerCancel, 3 | HCESession, 4 | NFCTagType4, 5 | NFCTagType4NDEFContentType, 6 | } from "react-native-hce"; 7 | import { HceOptions, HceReadHandler } from "../types/HceTypes"; 8 | 9 | /** 10 | * Class which provides methods for emulating a Ndef card with your phone 11 | */ 12 | export default class HceTools { 13 | removeListener?: HCESessionEventListenerCancel; 14 | stopEmulation: () => Promise; 15 | hceSession?: HCESession; 16 | 17 | constructor() { 18 | HCESession.getInstance().then((instance) => (this.hceSession = instance)); 19 | this.stopEmulation = async () => { 20 | try { 21 | await this.hceSession?.setEnabled(false); 22 | this.removeListener && this.removeListener(); 23 | } catch (err) { 24 | throw err; 25 | } 26 | }; 27 | } 28 | 29 | async startEmulation(options: HceOptions, onRead: HceReadHandler) { 30 | const tag = new NFCTagType4({ 31 | type: NFCTagType4NDEFContentType.Text, 32 | content: options.content, 33 | writable: options.writable, 34 | }); 35 | 36 | try { 37 | this.hceSession = await HCESession.getInstance(); 38 | this.hceSession.setApplication(tag); 39 | await this.hceSession.setEnabled(true); 40 | this.removeListener = this.hceSession.on( 41 | HCESession.Events.HCE_STATE_READ, 42 | () => { 43 | onRead(); 44 | this.stopEmulation(); 45 | } 46 | ); 47 | } catch (err) { 48 | throw err; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { HCESession, HCESessionEventListenerCancel } from "react-native-hce"; 2 | import { NdefTagInfo } from "./src/types/NdefTypes"; 3 | import { HceOptions, HceReadHandler } from "./src/types/HceTypes"; 4 | 5 | declare module "react-native-nfc-sdk" { 6 | /** 7 | * Class that provides methods for reading and writing ndef tags 8 | */ 9 | class NdefTools { 10 | /** 11 | * Method for cancelling the current listener 12 | */ 13 | cancelRequest: () => void; 14 | 15 | /** 16 | * Function that listens until a Ndef tag is detected in the device's NFC reader. Then returns the id of the 17 | * Ndef tag and its decoded payload (as "content") 18 | * 19 | * @return {Promise} An object containing the id and the decoded payload of the NFC tag or undefined in case there was an error reading the tag (The error will be thrown). 20 | */ 21 | readTag(): Promise; 22 | 23 | /** 24 | * Function that writes a value as the payload of a Ndef tag and returns a boolean wether the card was written 25 | * successfully, errors may be thrown 26 | * 27 | * @param value The value that will be written to the Ndef card 28 | * 29 | * @returns A boolean wether the card was written successfully or not 30 | */ 31 | writeTag(value: string): Promise; 32 | } 33 | 34 | /** 35 | * Class that provides methods for emulating a Ndef card with your phone 36 | */ 37 | class HceTools { 38 | /** 39 | * Method to remove the current read listener 40 | */ 41 | removeListener?: HCESessionEventListenerCancel; 42 | /** 43 | * Method to stop the current card emulator (triggers removeListener) 44 | */ 45 | stopEmulation: () => Promise; 46 | /** 47 | * The singleton hce session provided by react-native-hce 48 | */ 49 | hceSession?: HCESession; 50 | 51 | /** 52 | * Method to start a hce emulation and execute a callback once it's read 53 | * 54 | * @param options object that receives a content and a writable boolean to determine what's going to be the 55 | payload of the emulated card when read and wether it's writable by other phones or not 56 | * @param onRead callback that will be executed once the emulated card is read (triggers stopEmulation) 57 | */ 58 | startEmulation(options: HceOptions, onRead: HceReadHandler): Promise; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/classes/NdefTools.ts: -------------------------------------------------------------------------------- 1 | import NfcManager, { Ndef, NfcTech } from "react-native-nfc-manager"; 2 | import { NdefTagInfo } from "../types/NdefTypes"; 3 | 4 | /** 5 | * Class which provides methods for reading and writing ndef tags 6 | */ 7 | export default class NdefTools { 8 | /** 9 | * Method for cancelling the current listener 10 | */ 11 | cancelRequest: () => void; 12 | constructor() { 13 | this.cancelRequest = () => NfcManager.cancelTechnologyRequest(); 14 | } 15 | 16 | /** 17 | * Function which listens until a Ndef tag is detected in the device's NFC reader. Then returns the id of the 18 | * Ndef tag and its decoded payload (as "content") 19 | * 20 | * @return { Promise } An object containing the id and the decoded payload of the NFC tag or undefined in case there was an error reading the tag (The error will be logged). 21 | */ 22 | async readTag(): Promise { 23 | try { 24 | await NfcManager.requestTechnology(NfcTech.Ndef); // Request the hardware to listen for a specific technology 25 | const tag = await NfcManager.getTag(); // Opening the event listener to look for a NFC card to be read 26 | 27 | if (tag && tag.id) { 28 | return { 29 | id: tag.id, 30 | content: Ndef.uri 31 | .decodePayload(tag.ndefMessage[0].payload as unknown as Uint8Array) 32 | .replace("https://www.en", ""), // Decoding the payload since is a buffer and we want to return the decoded payload 33 | }; 34 | } else { 35 | throw new Error("Reading the tag was not possible."); 36 | } 37 | } catch (err) { 38 | throw err; 39 | } finally { 40 | this.cancelRequest(); // Cancel the NFC request at the end of the operation 41 | } 42 | } 43 | 44 | /** 45 | * Function that writes a value as the payload of a Ndef tag and returns a boolean wether the card was written 46 | * successfully 47 | * 48 | * @param value The value that will be written to the Ndef card 49 | * 50 | * @returns A boolean wether the card was written successfully or not 51 | */ 52 | async writeTag(value: string): Promise { 53 | try { 54 | await NfcManager.requestTechnology(NfcTech.Ndef); 55 | 56 | const bytes = Ndef.encodeMessage([Ndef.textRecord(value)]); // Encoding as buffer the string we recieved 57 | 58 | if (bytes) { 59 | await NfcManager.ndefHandler.writeNdefMessage(bytes); // Writing the buffer in the Ndef tag 60 | return true; 61 | } 62 | 63 | return false; 64 | } catch (err) { 65 | throw err; 66 | } finally { 67 | this.cancelRequest(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-nfc-sdk 2 | 3 | [![NPM Version](https://badgen.net/badge/npm/v0.3.4-beta/yellow)](https://www.npmjs.com/package/react-native-nfc-sdk) 4 | [![Dev supported?](https://badgen.net/badge/dev_support/no/red)](https://github.com/loridev/react-native-nfc-sdk/graphs/commit-activity) 5 | [![License](https://badgen.net/badge/licence/GPL-3.0/orange)](https://github.com/loridev/react-native-nfc-sdk/blob/main/LICENSE) 6 | 7 | **react-native-nfc sdk** is a package that aims to simplify the way NFC apps are coded in React Native in order to make it easier for developers to implement their solutions and develop their ideas using the NFC technology. 8 | 9 | ## Behind the scenes 10 | 11 | This package simplifies operations in the packages [react-nfc-manager](https://github.com/revtel/react-native-nfc-manager) and [react-native-hce](https://github.com/appidea/react-native-hce). If it does not cover your specific case, create an [issue](https://github.com/loridev/react-native-nfc-sdk/issues) or head to the respective package depending on what technology you're trying to use. 12 | 13 | ## Good to know 14 | 15 | - Only tested in Android, so it may give you problems on other platforms 16 | 17 | - It should cover the vast majority of cases where NFC technology is used 18 | 19 | - Has utilities for both NFC and HCE technologies 20 | 21 | - Uses NDEF (NFC Data Exchange Format) for compatibility purposes 22 | 23 | - This package **WON'T WORK** in a Expo *Managed* React Native app 24 | 25 | ## What can I do with this package? 26 | 27 | - Read NFC tags' IDs and decoded payload 28 | 29 | - Write a custom payload to a NFC tag 30 | 31 | - Use the HCE technology to use your phone as a NFC tag 32 | 33 | 34 | 35 | 36 | ## Getting Started 37 | 38 | Note: Every time a relative route is shown in this documentation, corresponds to the route from **your react native project root**. 39 | 40 | ### Installing 41 | 42 | ```shell 43 | npm i react-native-nfc-sdk 44 | ``` 45 | 46 | #### Installing dependencies (from v0.3.1) 47 | ```shell 48 | npm i react-native-nfc-manager react-native-hce 49 | ``` 50 | 51 | ### Permission settings 52 | 53 | Since this was only tested on Android, there will only be documentation on how to do the permissions configuration on Android. You'll need to enable different permissions depending on if you're using the NFC or HCE technologies. 54 | 55 | #### NFC 56 | 57 | Go to the file `android/app/src/main/AndroidManifest.xml` and add this line before the `application` tag: 58 | 59 | ```xml 60 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ``` 69 | 70 | #### HCE 71 | 72 | Create a new file `android/app/src/main/res/xml/aid_list.xml` (you have to create the `aid_list.xml` file) and put the following content in the file: 73 | 74 | ```xml 75 | 78 | 80 | 81 | 82 | 83 | ``` 84 | 85 | Go to the file `android/app/src/main/AndroidManifest.xml` and add this line before the `application` tag: 86 | 87 | ```xml 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | ``` 99 | 100 | In the same `AndroidManifest.xml` file, add the following lines in the `application` block so the app can communicate with the HCE service: 101 | 102 | ```xml 103 | 110 | 111 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | 123 | 124 | 127 | 128 | 129 | 130 | ``` 131 | 132 | ### Usage 133 | 134 | #### NFC 135 | 136 | ```javascript 137 | import React from 'react'; 138 | import { View, Text, StyleSheet, Button } from 'react-native'; 139 | import { NdefTools } from 'react-native-nfc-sdk'; 140 | 141 | export default function App () { 142 | const ndef = new NdefTools(); 143 | const [tagContent, setTagContent] = React.useState(''); 144 | const [writeStatus, setWriteStatus] = React.useState(''); 145 | 146 | const readTag = async () => { 147 | // The read tag function sets an event handler for when a tag 148 | // is read and returns a js object of 149 | // {id: 'nfc-id', content: 'decoded-payload'} 150 | try { 151 | const tag = await ndef.readTag(); 152 | if (tag) setTagContent(tag.content); 153 | } catch (err) { 154 | console.err(err); 155 | } 156 | ndef.cancelRequest(); // Cancel the request to the nfc hardware 157 | } 158 | 159 | const writeTag = async () => { 160 | // The write tag function sets an event handler for when a tag 161 | // is passed and tries to write the content passed by parameter 162 | // as the payload. It returns a boolean wether the write action 163 | // was successful or not. 164 | try { 165 | if (await ndef.writeTag('NFC Hello World')) { 166 | setWriteStatus('Tag written successfully!'); 167 | } else { 168 | setWriteStatus('There was an error writing the tag :('); 169 | } 170 | } catch (err) { 171 | console.err(err); 172 | } 173 | ndef.cancelRequest(); // Cancel the request to the nfc hardware 174 | } 175 | 176 | return ( 177 | 178 |