Search code examples
androidreact-nativemifare

React Native: Reading data from two sectors at once throws "Transceive fail" error


I am currently working on a simple app, where the user scans the NFC tag and receives the data stored on it on the screen.

I followed this example and everything works fine when I try to authenticate one sector and read & write data from it but, but when I try to read data from two sectors at once I receive the "Transceive fail" error.

I am testing the app on a Samsung J6 device using a Mifare 1k Card.

Here is my code:

this.state = {
      keyAorB: KeyTypes[1], // 'B'
      keyToUse: 'FFFFFFFFFFFF',
      sectors: [3, 4],
      ...
};

authenticateSector = (sector) => {
    // Convert the key to a UInt8Array
    const key = [];
    for (let i = 0; i < this.state.keyToUse.length - 1; i += 2) {
      key.push(parseInt(this.state.keyToUse.substring(i, i + 2), 16));
    }

    if (this.state.keyAorB === KeyTypes[0]) {
      return NfcManager.mifareClassicAuthenticateA(sector, key);
    } else {
      return NfcManager.mifareClassicAuthenticateB(sector, key);
    }
};

read = (sector) => {
    return NfcManager.mifareClassicGetBlockCountInSector(sector)
      .then(() => NfcManager.mifareClassicReadSector(sector))
      .then((tag) => {
        ...
      })
      .then(() => NfcManager.mifareClassiacSectorToBlock(sector))
      .then(block => NfcManager.mifareClassicReadBlock(block))
      .catch((err) => console.warn(err))
};

readBulk = () => {
    this.state.sectors.forEach(async (s) => {
      const sector = parseInt(s);
      await this.authenticateSector(sector)
        .then(() => this.read(sector))
        .then(() => this.cleanUp())
        .catch((err) => console.warn(err));
    });
}

After debugging, I found out where the problem lies, namely: as far as I know, a sector must be authenticated and then read from, but, in my case, both sectors are authenticated and then the read function is called for both of them which doesn't work.

My question is: why does this actually happen? Shouldn't my code work in a way that a sector is authenticated (which returns a Promise), then the data is read from it and cleaned up afterwards?

Any help is appreciated! :)


Solution

  • .forEach() doesn't wait for async callbacks (see this article), so your readBulk function is actually calling this.read() on all sectors at once, without waiting for cleanup. forEach is just running through and firing off callbacks for all of the elements in this.sectors.

    To wait for each auth/read/cleanUp cycle before continuing, you can refactor it like so:

    
    readBulk = async () => {
      for (const s of this.state.sectors) {
        const sector = parseInt(s)
        try {
          await this.authenticateSector(sector)
          await this.read(sector)
          await this.cleanUp()
        } catch (err) {
          console.warn(err)
        }
      }
        
    }