Thirdly, “Privacy — NFC Scan Usage Description” must be added to the Info.plist. Otherwise, app is terminated by libsystem_kernel.dylib __pthread_kill with no error message at Xcode console.

Info.plist screenshot

If NFC in your app is an optional feature and you probably wants those device without NFC chip to run your app, the CoreNFC.framework MUST be set to “Optional” in iOS 12.

Else, app will be crash at start with the following message showing “CoreNFC.framework” cannot be loaded.

dyld: Library not loaded: /System/Library/Frameworks/CoreNFC.framework/CoreNFC Referenced from: /var/containers/Bundle/Application/DF6D870C-4928-40F6-A242-BAB9C07B3737/BaseProject.app/BaseProject Reason: image not found

Let’s start coding now!

I mplementing NFCNDEFReaderSessionDelegate

How to interpret the NFC information from [NFCNDEFMessage] Object?

The NFC information is stored inside the payload of record within the NFCNDEFMessage object.

Each NFCNDEFMessage object contains a list of “record” and each record contains:

1. “payload” in form of NSData and contains tag message

2. “typeNameFormat”

3. “type”

4. “identifier”

Example:

NFC tag original message: “abc”

Original message in ASCII code:

— 0: 97 [a]

— 1 : 98 [b]

— 2 : 99 [c]

— 0: 97 [a] — 1 : 98 [b] — 2 : 99 [c] Scanned payload in record of NFCNDEFMessage

— 0 : 2 [START OF TEXT] in ASCII table.

— 1 : 101 [e]

— 2 : 110 [n]

— 3 : 97 [a]

— 4 : 98 [b]

— 5 : 99 [c]

Why there is “en” in front of the original message “abc”?

“All language codes MUST be done according to RFC 3066 [RFC3066]. The language code MAY NOT be omitted. The language code length is encoded in the six least significant bits of the status byte. Thus it is easy to find by masking the status byte with the value 0x3F.”

— NFC Data Exchange Format (NDEF) Technical Specification, NFC Forum

Thus, the payload of record has to be advanced by 3 before converting to a String for displaying!

record.payload.advanced(by:3)

Handling result at the didDetectNDEFs

Case 1 (init with parameter — invalidateAfterFirstRead: true) :

If NFC reader session is initialised with invalidateAfterFirstRead equals true, NFC tag reading dialog will be automatically dismissed after tag is successfully detected and the blue-tick animation is completed (last for around 3 seconds).

Due to this 3-second time gap, UIAlertController is presented but covered by the NFC tag reading dialog completely, and user cannot interact with any UIAlertAction . This results in a bad user experience. See below gif:

Case 2 (init with parameter — invalidateAfterFirstRead: false) :

NFC reading tag dialog can be dismissed manually if NFCNDEFReaderSession is initialised with invalidateAfterFirstRead equals false.

However, the NFC session can actually be dismissed after the blue-tick animation is completed. Thus, calling nfcSession.invalidate() right before showing UIAlertController does not guarantee the NFC tag reading dialog is actually dismissed. There is an around 0.5 second time gap for this. Thus, the UIAlertController is still shown behind the NFC tag reading dialog for a noticeable short period of time. User experience is not perfect.

Case 3 (init with parameter — invalidateAfterFirstRead: false + 1s delay) :

Finally, the solution to provide the best user experience is to give an around 1 second delay to the DispatchQueue to ensure that the NFC tag reading dialog is completely dismissed before showing an UIAlertController.

Minor reminders:

The NFCNDEFReaderSession has a default 60 seconds time out for reading NFC tag. Below is the error message for time out.

Session is invalidated due to maximum session timeout

2. Alert message in the NFC tag reading dialog is optimised for 3 lines of wordings. The 4th line is truncated at the end.

3. Titles of NFC tag reading dialog and cancel button are not customisable and the locale is fixed to the device locale.

Conclusion: