> ## Documentation Index
> Fetch the complete documentation index at: https://react-native-nfc-kit.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Reading FeliCa

> Suica, PASMO and Octopus, and the system codes iOS filters on at the radio.

Suica, PASMO, Octopus, and the rest of the FeliCa family.

```ts theme={null}
import { nfc } from 'react-native-nfc-kit';
import { polling, readWithoutEncryption } from 'react-native-nfc-kit/protocols';

await nfc.withTag({ tech: ['felica'] }, async (tag) => {
  if (!tag.is('felica')) throw new Error('Not a FeliCa card');

  const transport = (packet: Uint8Array) => tag.transceive(packet);

  const { idm } = await polling(transport, 0x12fc);
  const blocks = await readWithoutEncryption(
    transport,
    idm,
    [0x090f],
    [{ block: 0 }, { block: 1 }],
  );
});
```

Every FeliCa command addresses the card by its **IDm**, which is why it is passed
explicitly. `polling` obtains one; on iOS `tag.ios?.idm` carries it too. Full API in
[Tag protocols](/protocols).

## The iOS rule

<Warning>
  As with ISO 7816 AIDs, **iOS filters FeliCa at the radio**: a card whose system code is not
  declared in Info.plist never reaches the app. No error, no event.
</Warning>

`FFFF` is the wildcard and matches any system code. It is the right value while
you are working out which system code a card uses, and usually the wrong value to
ship, because it means every FeliCa card in the user's wallet is a candidate.

| System code | What it is             |
| ----------- | ---------------------- |
| `0003`      | The FeliCa standard    |
| `FE00`      | FeliCa Lite / Lite-S   |
| `12FC`      | NDEF over FeliCa       |
| `FFFF`      | Wildcard — matches all |

## Expo

```json app.json theme={null}
{
  "expo": {
    "plugins": [
      [
        "react-native-nfc-kit",
        {
          "readerUsageDescription": "Hold your card near the top of the phone",
          "ios": { "felicaSystemCodes": ["12FC"] }
        }
      ]
    ]
  }
}
```

Each code is exactly two bytes of plain hex. The plugin rejects any other length,
since a wrong-length code is accepted by the build and matches nothing on device.

## Bare React Native

### `ios/<App>/<App>.entitlements`

```xml theme={null}
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
  <string>TAG</string>
</array>
```

### `ios/<App>/Info.plist`

```xml theme={null}
<key>NFCReaderUsageDescription</key>
<string>Hold your device near an NFC tag to read it.</string>
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
  <string>12FC</string>
</array>
```

### `android/app/src/main/AndroidManifest.xml`

```xml theme={null}
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<activity android:name=".MainActivity" android:launchMode="singleTop">
  <!-- your existing intent filters stay here -->
</activity>
```

## Platform differences worth knowing

Both platforms reach FeliCa, but not identically:

<AccordionGroup>
  <Accordion title="Polling" icon="tower-broadcast">
    On iOS the session must include the `iso18092` polling option; asking for `tech: ['felica']`
    sets it for you.
  </Accordion>

  <Accordion title="IDm and PMm" icon="fingerprint">
    iOS exposes both on the tag's `ios` facet. Android gives you the IDm as the tag id and the PMm
    through a `polling` command.
  </Accordion>

  <Accordion title="Frame size" icon="ruler">
    Android's `maxTransceiveLength` is chipset-dependent and worth checking before sending a long
    multi-block read.
  </Accordion>
</AccordionGroup>
