semlette / nfc_in_flutter

Cross-platform flutter plugin for reading and writing NFC tags. Not maintained anymore - not looking for new maintainer, fork instead.
MIT License
120 stars 119 forks source link

Writing to tag in iOS throw an exception PlatformException(NFCTagUnavailable, the tag is unavailable for writing, null) #38

Open Moujarkash opened 4 years ago

Moujarkash commented 4 years ago

void _ndefWrite(NDEFMessage message) { if(!message.tag.writable){ result.value = NfcResponse.error(Strings.NFC_TAG_NOT_WRITABLE); return; }

List<NDEFRecord> newRecords = new List();
newRecords.add(NDEFRecord.plain(jsonEncode(widget._ticket)));

if (message.records.isNotEmpty) newRecords.addAll(message.records);

NDEFMessage newMessage = NDEFMessage.withRecords(newRecords);

message.tag.write(newMessage).then((value) {
  result.value = NfcResponse.success(null);
  widget.ticketsProvider.startTicket(widget._ticket.id);
}, onError: (error) {
  String errorMessage = Strings.SOMETHING_WRONG_HAPPENED_MESSAGE;

  if(error is Exception)
    errorMessage = error.toString();

  result.value = NfcResponse.error(errorMessage);
});

}

the Android version worked, the issue only appeared in iOS

James-A-White commented 4 years ago

A few insights from my testing:

1) it seems like timing might be one of the issues, but I cannot absolutely confirm this. I was able to get some successful writes using the following code:


      await Future<void>.delayed(const Duration(milliseconds: 5000));
      final Stream<NDEFMessage> stream = NFC.readNDEF(throwOnUserCancel: true);
      await Future<void>.delayed(const Duration(milliseconds: 5000));
      writeSub = stream.listen((NDEFMessage message) {
        final NDEFMessage newMessage = NDEFMessage.withRecords([NDEFRecord.text('hello world')]);
        try {
          message.tag.write(newMessage).then((dynamic dummy) {
            writeSub.cancel();
            verifyTag(newPoint, room.roomId);
          });
        } catch (e) {
          writeSub.cancel();
          String errStr2 = e.toString();
          String exStr2 = e.runtimeType.toString();
          int xxx2 = 0;
          print(errStr2);
          print(exStr2);
        }
      }, onError: (Object error) {
        writeSub.cancel();
        print('On error called');
      }, onDone: () {
        writeSub.cancel();
        print('On done called');
      }, cancelOnError: true);

2) When writes fail, they leave the tag empty. When I erase a tag with NFCTools, it places an empty Record 0 on the tag, after a failed write, this record is gone.

3) There may be some indication in my testing that the more complex the message, the higher the failure rate. I was able to successfully write "hello world" more often than I was able to successfully write two records with the following text:

Record 0 contains: R2W:eRfKwMPjeX Record 1 contains: roomId:FF288C53-FF8E-427E-97C5-83238B7646AE

NOTE: It works fine in Android, so I don't suspect that the content of record itself is a problem, although the length might be an issue (14 + 43 = 57 bytes)?

Any help would be greatly appreciated!

James-A-White commented 4 years ago

Ahhh… an interesting insight from my testing. When I write using Android, the payload is encoded with UTF-8 (and everything works fine). When I write with iOS, the payload is encoded with UTF-16.

Android can read either the UTF-8 or UTF-16 in the data field, although the payload field does not decode correctly. iOS can read only the UTF-8 encoded tags but not the UTF-16 tags.

Using NFC Tools, I can read both tags (that’s how I know that iOS is encoding using UTF-16) and the payload is correct for both encoding schemes. When reading the UTF-16 tag on Android, here’s what I get; notice that the data field is correct, but the payload field has what appears to be Japanese characters.

image

When I read the same UTF-16 tag on iOS, the tag reads as empty.

So, it looks like there may be two issues here. One is the ability to read tags with UTF-16 on both iOS and Android platforms. The other is the ability to have a deterministic write function across both the iOS and Android platforms so that the behavior is the same.

I have worked around the problem by adding NDEF records to the message using the "plain" factory constructor as opposed to using the "text" factory constructor.

NDEFRecord.plain(myMessage);

versus

NDEFRecord.text(myMessage);

By doing this, no text encoding is applied and all works as expected.

mavbcn commented 4 years ago

I'm having the same problems in IOS, In android all works fine, but not in iOS. Any good advice? I need to read a tag, and update its value (write a code) thanks so much

mahirandigital commented 8 months ago

I'm having the same problems in IOS, In android all works fine, but not in iOS. Any good advice? I need to read a tag, and update its value (write a code) thanks so much

do you get how to solve? same problem to me