Getting Started with Core NFC
In this tutorial, you’ll learn how to use CoreNFC to connect wirelessly to other devices or NFC tags. By Andy Pereira.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started with Core NFC
25 mins
- Getting Started
- Writing to Your First Tag
- Understanding NDEF
- Detecting Tags
- Handling the Tag
- Creating the Payload
- Using NDEF Payload Types
- Reading the Tag
- Writing Different Data Types
- Writing Custom Data Instead of Strings
- Checking Tag Capacity
- Reading Your Custom Data
- Modifying Content
- Where to Go From Here?
Handling the Tag
Once you know you have one tag, you’ll probably want to do something with it. Add the following code after your guard
statement within readerSession(_:didDetect:)
:
// 1
session.connect(to: tag) { error in
if let error = error {
self.handleError(error)
return
}
// 2
tag.queryNDEFStatus { status, _, error in
if let error = error {
self.handleError(error)
return
}
// 3
switch (status, self.action) {
case (.notSupported, _):
session.alertMessage = "Unsupported tag."
session.invalidate()
case (.readOnly, _):
session.alertMessage = "Unable to write to tag."
session.invalidate()
case (.readWrite, .setupLocation(let locationName)):
self.createLocation(name: locationName, with: tag)
case (.readWrite, .readLocation):
return
default:
return
}
}
}
Here’s what you’re doing in the code above:
- Connect to the detected tag, using the current
NCFNDEFReaderSession
. You need to do this step to perform any reading or writing to the tag. Once connected, it will call its completion handler, with any error that might occur. - Query the tag for its NDEF status to see if the NFC device is supported. The status must be
readWrite
for the purpose of your NeatoCache app. - Switch over the status and the NFC action and determine what should be done based on their values. Here, you’re attempting to set up a tag to have a location name using
createLocation(name:with:)
, which doesn’t exist yet, so you’ll encounter a compilation error. No worries though, you’ll add it in a moment. Similarly, thereadLocation
action is also not handled yet.
Creating the Payload
Up to this point, you’ve worked with finding a tag, connecting to it and querying its status. To finish setting up writing to a tag, add the following block of code to the end of NFCUtility.swift:
// MARK: - Utilities
extension NFCUtility {
func createLocation(name: String, with tag: NFCNDEFTag) {
// 1
guard let payload = NFCNDEFPayload
.wellKnownTypeTextPayload(string: name, locale: Locale.current)
else {
handleError(NFCError.invalidated(message: "Could not create payload"))
return
}
// 2
let message = NFCNDEFMessage(records: [payload])
// 3
tag.writeNDEF(message) { error in
if let error = error {
self.handleError(error)
return
}
self.session?.alertMessage = "Wrote location data."
self.session?.invalidate()
self.completion?(.success(Location(name: name)))
}
}
}
Here’s what you’re doing in the code above:
- Create a textual
NFCNDEFPayload
. As stated earlier, this is akin to an NDEF Record. - Make a new
NFCNDEFMessage
with the payload so that you can save it to the NFC device. - Finally, write the message to the tag.
Using NDEF Payload Types
NFCNDEFPayload
supports several different types of data. In this example, you’re using wellKnownTypeTextPayload(string:locale:)
. This is a fairly simple data type that uses the string and the device’s current locale. A few of the other data types hold more complex information. Here’s the full list:
- Empty
- Well-Known
- MIME media-type
- Absolute URI
- External
- Unknown
- Unchanged
- Reserved
Also note that a type can have subtypes. Well-known, for example, has the subtypes of Text and URI.
Also note that a type can have subtypes. Well-known, for example, has the subtypes of Text and URI.
You’re getting really close! All that’s left is to hook up your UI to your new code. Go to AdminView.swift and replace the following code:
Button(action: {
}) {
Text("Save Location…")
}
.disabled(locationName.isEmpty)
With this one:
Button(action: {
NFCUtility.performAction(.setupLocation(locationName: self.locationName)) { _ in
self.locationName = ""
}
}) {
Text("Save Location…")
}
.disabled(locationName.isEmpty)
This will make a call to set up your location with the text found in the text field.
Build and run, switch to the Admin tab of the app, enter a name and select Save Location….
You’ll see the following:
Once you place your phone on your NFC tag, you’ll see a message that your location was successfully saved.
Reading the Tag
Great! Now that you now have an app that can write a string to a tag, you’re ready to build support for reading it. Go back to NFCUtility.swift and find the following code in readerSession(_:didDetect:)
.
case (.readWrite, .readLocation):
return
Now, replace it with this:
case (.readWrite, .readLocation):
self.readLocation(from: tag)
Time to implement that readLocation(from:)
method. Add the following to the Utilities extension that contains createLocation(name:with:)
:
func readLocation(from tag: NFCNDEFTag) {
// 1
tag.readNDEF { message, error in
if let error = error {
self.handleError(error)
return
}
// 2
guard
let message = message,
let location = Location(message: message)
else {
self.session?.alertMessage = "Could not read tag data."
self.session?.invalidate()
return
}
self.completion?(.success(location))
self.session?.alertMessage = "Read tag."
self.session?.invalidate()
}
}
This addition should look somewhat familiar to you, as it is very similar to how your wrote to the tag.
- First, you initiate reading a tag. If it can be read, it will return any messages it finds.
- Next, you attempt to create a Location out of the message data, if you have one. This uses a custom initializer that accepts a
NFCNDEFMessage
and pulls the name off it. If you’re curious, you can find that initializer in LocationModel.swift.
Finally, open VisitorView.swift, and in scanSection
, replace the following code:
Button(action: {
}) {
Text("Scan Location Tag…")
}
With this one:
Button(action: {
NFCUtility.performAction(.readLocation) { location in
self.locationModel = try? location.get()
}
}) {
Text("Scan Location Tag…")
}
You’re all set to read data from your tag. Build and run.
On the Visitors tab, tap Scan Location Tag…. You’ll see the following, with your location name now in the UI:
Writing Different Data Types
While writing strings may work perfectly for some use cases, you may find that you want to write other kinds of data to tags.
To prepare for this, add the following to NFCUtility.swift, within the Utilities extension:
private func read(
tag: NFCNDEFTag,
alertMessage: String = "Tag Read",
readCompletion: NFCReadingCompletion? = nil
) {
tag.readNDEF { message, error in
if let error = error {
self.handleError(error)
return
}
// 1
if let readCompletion = readCompletion,
let message = message {
readCompletion(.success(message))
} else if
let message = message,
let record = message.records.first,
let location = try? JSONDecoder()
.decode(Location.self, from: record.payload) {
// 2
self.completion?(.success(location))
self.session?.alertMessage = alertMessage
self.session?.invalidate()
} else {
self.session?.alertMessage = "Could not decode tag data."
self.session?.invalidate()
}
}
}
This new method for reading tags will be the entry point for most of your activities from here on out. As you can see, it still reads the tag, just like before. However, once it reads the tag, it will do one of two things:
- Call a completion handler and pass the message to it. This will be useful for chaining multiple NFC tasks together.
- Decode the payload so that you can parse the tag’s records. You’ll come back to this in a little bit.