Reading and Parsing NFC tag on iOS 11
Apple finally opens iPhone’s NFC chip. With iOS 11, apps on iPhone 7 and iPhone 7 plus can read NFC tag using Core NFC. (Writing is not allowed!) It adds more possibilities to apps. For example, we can use iPhone to tap a NFC tag to read business car, make phone call, open web page, create an email, open another app, or share Wi-Fi. Apple itself uses NFC to pair up HomeKit devices.
Using Core NFC, you can read Near Field Communication (NFC) tags of types 1 through 5 that contain data in the NFC Data Exchange Format (NDEF). Let’s write some code.
Firstly, import Core NFC library in your iOS project.
import CoreNFC
Secondly, make YourViewController
class conform to NFCNDEFReaderSessionDelegate
, like this:
class YourViewController: UIViewController, NFCNDEFReaderSessionDelegate {
Thirdly, start a NFCNDEFReaderSession
when you tap a button.
@IBAction func didTapScanNFCTagButton(_ sender: Any) {
let session = NFCNDEFReaderSession(delegate: self, queue: DispatchQueue.main, invalidateAfterFirstRead: false)
session.begin()
}
Lastly, get the NFC NDEF message in the callback.
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for payloadRecord in message.records {
// Handle payloadRecord here
}
}
}
Here, payloadRecord
’s type is NFCNDEFPayload
. But as you can see in the following picture, Apple’s doc does not give us much detail. typeNameFormat
could be nfcWellKnown
, media
or several other types. But identifier
, payload
and type
are all Data
which could be anything.
Let’s try to read a simplest NFC tag which contains plain text only. I have empty NFC tags and wrote plain text “hello” into a NFC tag with a free app NFC TagWriter by NXP on an old Android phone Samsung Galaxy Note 3. (Yes, Android phones have NFC for years.). As you can see below, it supports writing many types of data, such as business card, link, email, telephone number, geo location, plain text, etc.
For the plain text tag, typeNameFormat
isnfcWellKnown
. Let’s assume identifier
, payload
and type
are all UTF-8 string. After we convert Data
to string and print them out, we will see identifier
is empty, type
is “T”, payload
is “enhello”. Obviously “T” means Text. But why is there “en” before “hello”?
The actual content of payload
is the following in hex:
02 65 6E 68 65 6C 6C 6F |.enhello|
The structure of plain text payload is more complex than you expected. It consists of 1 status byte, 2 or 5 byte language code, and multiple bytes in UTF-8 or UTF-16. Here is the explaination (from Austin Blackstone’s post. Many thanks!).
|------------------------------|
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0|
|------------------------------|
|UTF| 0 | Length of Lang Code | 1 byte Text Record StatusByte
|------------------------------|
| Lang Code | 2 or 5 byte, multi-byte lang code
|------------------------------|
| Text | Multiple Bytes in UTF-8 or UTF-16
|------------------------------|
For our plain text “hello” payload, the first byte is 02
in hex. It is 00000010
in binary. The No. 7 bit is 0
which means the text is encoded in UTF-8. The 0–5 bits show the length of language code. The length is 2. The following 2 bytes “en” is the language code. The real text is the remaining bytes “hello”.
An NDEF message consists of header and payload. Core NFC parses message header only, but does not parse message payload. You can see even if we only read a plain text NFC tag using Core NFC, it is not so easy as we expected. We can imagine reading NFC tag with other types of payload is much more challenging.
I decided to accept the challenge. I made VYNFCKit, an open source NFC NDEF message payload parsing library. VYNFCKit supports parsing almost all data types TagWriter supports, including Business card, Link, WiFi, Email, Telephone number, Geo location, Plain text and SMS. I did not have time to handle Bluetooth
and Launch Application
. Launch Application
seems for Android only.
Parsing NFC NDEF message payload with VYNFCKit becomes easy.
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for payload in message.records {
guard let parsedPayload = VYNFCNDEFPayloadParser.parse(payload) else {
continue
}
if let parsedPayload = parsedPayload as? VYNFCNDEFTextPayload {
// parsedPayload.text
} else if let parsedPayload = parsedPayload as? VYNFCNDEFURIPayload {
// parsedPayload.uriString
} else if let parsedPayload = parsedPayload as? VYNFCNDEFTextXVCardPayload {
// parsedPayload.text
} else if let sp = parsedPayload as? VYNFCNDEFSmartPosterPayload {
for textPayload in sp.payloadTexts {
if let textPayload = textPayload as? VYNFCNDEFTextPayload {
//textPayload.text
}
}
// sp.payloadURI.uriString
} else if let wifi = parsedPayload as? VYNFCNDEFWifiSimpleConfigPayload {
// wifi.credential.ssid, wifi.credential.networkKey, wifi.credential.macAddress,
// wifi.credential.authType,
// wifi.credential.encryptType
}
}
}
}
Please refer to VYNFCKit for detailed steps. Example projects in both Objective-C and Swift are provided. Welcome to try VYNFCKit, report bugs, and send me pull requests. Star ⭐️ it if it helps you a little. 🍺