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.

4.3 (3) · 1 Review

Download materials
Save for later
Share

Near Field Communication (NFC) is a technology for short-range wireless devices to share data with other devices or trigger actions on those devices. Built using a radio frequency field, it allows devices that don’t have any power to store small pieces of data while also enabling other powered devices to read that data.

iOS and watchOS devices have had NFC hardware built into them for several years now. In fact, Apple Pay uses this technology to interact with payment terminals at stores. However, developers weren’t able to use NFC hardware until iOS 11.

Apple upped its NFC game in iOS 13 by introducing Core NFC. With this new technology, you can program iOS devices to interact with the connected world around them in new ways. This tutorial will show you some of the ways you can use this technology. Along the way, you’ll learn how to:

  • Write standard information to an NFC tag.
  • Read that information.
  • Save custom information to a tag.
  • Modify the data already found on a tag.
  • A physical iOS device
  • An Apple developer account
  • NFC hardware that you can read from and write to. Many online retailers carry NFC tags at reasonable prices. You can usually get a pack of NFC tags for around $10 USD. Look for something in the description that states it is programmable or lists its storage capacity, typically 300 to 500 bytes. Any device with this approximate capacity is more than acceptable for this tutorial.
Important Note: To perform all the steps in this tutorial, you’ll need the following:
  • A physical iOS device
  • An Apple developer account
  • NFC hardware that you can read from and write to. Many online retailers carry NFC tags at reasonable prices. You can usually get a pack of NFC tags for around $10 USD. Look for something in the description that states it is programmable or lists its storage capacity, typically 300 to 500 bytes. Any device with this approximate capacity is more than acceptable for this tutorial.

Getting Started

To get started, download the tutorial projects using the Download Materials button at the top or bottom of this tutorial and open the starter project in the starter folder. With the project app, you’ll learn how to:

  • Set up an NFC tag as a “location”.
  • Scan a location tag to see its name and visitor log.
  • Add a visitor to a location tag.

Build and run. You’ll see the following:

App running after downloading materials

Writing to Your First Tag

To get started, select the NeatoCache project in the Project navigator. Then, go to Signing & Capability and select + Capability. Choose Near Field Communication Tag Reading from the list.

Showing how to add the NFC entitlement to your project
This will ensure your app’s provisioning profile is set up to use NFC.

Next, open your Info.plist and add the following entry:

  • Key: Privacy – NFC Scan Usage Description
  • Value: Use NFC to Read and Write data

You need this entry to communicate to your users what you’re using the NFC capability for, as well as to conform to Apple’s requirements about using NFC in your application.

Next, you’ll add a function that can perform the various NFC tasks your app will handle. Open NFCUtility.swift and add the following import and type aliases to the top of the file:

import CoreNFC

typealias NFCReadingCompletion = (Result<NFCNDEFMessage?, Error>) -> Void
typealias LocationReadingCompletion = (Result<Location, Error>) -> Void

You need to import CoreNFC to work with NFC. The type aliases provide the following functionality:

  • NFCReadingCompletion for completing generic tag reading tasks.
  • LocationReadingCompletion for reading a tag configured as a location

Next, add the following properties and method to NFCUtility:

// 1
private var session: NFCNDEFReaderSession?
private var completion: LocationReadingCompletion?

// 2
static func performAction(
  _ action: NFCAction,
  completion: LocationReadingCompletion? = nil
) {
  // 3
  guard NFCNDEFReaderSession.readingAvailable else {
    completion?(.failure(NFCError.unavailable))
    print("NFC is not available on this device")
    return
  }

  shared.action = action
  shared.completion = completion
  // 4
  shared.session = NFCNDEFReaderSession(
    delegate: shared.self,
    queue: nil,
    invalidateAfterFirstRead: false)
  // 5
  shared.session?.alertMessage = action.alertMessage
  // 6
  shared.session?.begin()
}

Don’t worry if you get a compilation error at this point due to not conforming to NFCNDEFReaderSessionDelegate, you’ll fix this momentarily.

Here’s what you just did:

  1. You add session and completion properties to store the active NFC reading session and its completion block.
  2. Add a static function as an entry point for your NFC reading and writing tasks. You’ll use singleton-styled access to this function and to NFCUtility, in general.
  3. Make sure the device supports NFC reading. Otherwise, complete with an error.
  4. Create an NFCNDEFReaderSession, which represents the active reading session. You also set the delegate to be notified of the various events of NFC reading session.
  5. You set the alertMessage property on the session so that it displays that text to the user, within the NFC modal.
  6. Start the reading session. When called, a modal will present to the user with any instructions you set in the previous step.

Understanding NDEF

Notice that the code above introduces another acronym, NDEF, which stands for NFC Data Exchange Format. It’s a standardized format for writing to or reading from an NFC device. The two pieces of NDEF you’ll use are:

  • NDEF Record: This contains your payload value, such as a string, URL or custom data. It also contains information about that payload value, like length and type. This information is the NFCNDEFPayload within CoreNFC.
  • NDEF Message: This is the data structure that holds NDEF records. There can be one or more NDEF records within an NDEF message.

Detecting Tags

Now that you’ve set up your NFCReaderSession, it’s time to conform NFCUtility as its delegate, so you can get notified of the various events that occur during a reading session.

Add the following code to the bottom of NFCUtility.swift:

// MARK: - NFC NDEF Reader Session Delegate
extension NFCUtility: NFCNDEFReaderSessionDelegate {
  func readerSession(
    _ session: NFCNDEFReaderSession,
    didDetectNDEFs messages: [NFCNDEFMessage]
  ) {
    // Not used
  }
}

You’ll add more to this extension in just a second, but note here that you won’t do anything with readerSession(_:didDetectNDEFs:) in this tutorial. You’re only adding it here since it’s mandatory to conform to the delegate protocol.

The more you interact with NFC technology, the more you’ll see the possibility of encountering errors at various stages of the reading and writing process. Add the following method to your new extension to capture those errors:

private func handleError(_ error: Error) {
  session?.alertMessage = error.localizedDescription
  session?.invalidate()
}

The first line of code should look familiar to you. It will present the error message to the user in the NFC modal view. In the event of an error, you’ll also invalidate the session to terminate it and allow the user to interact with the app again.

Next, add the following method to the extension to deal with errors from your NFC reading session:

func readerSession(
  _ session: NFCNDEFReaderSession,
  didInvalidateWithError error: Error
) {
  if let error = error as? NFCReaderError,
    error.code != .readerSessionInvalidationErrorFirstNDEFTagRead &&
      error.code != .readerSessionInvalidationErrorUserCanceled {
    completion?(.failure(NFCError.invalidated(message: 
      error.localizedDescription)))
  }

  self.session = nil
  completion = nil
}

Adding this delegate method will clear up any compilation errors you’ve encountered up to this point.

Finally, add this last method to your extension to deal with the possible detection of NFC tags:

func readerSession(
  _ session: NFCNDEFReaderSession,
  didDetect tags: [NFCNDEFTag]
) {
  guard 
    let tag = tags.first,
    tags.count == 1 
    else {
      session.alertMessage = """
        There are too many tags present. Remove all and then try again.
        """
      DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(500)) {
        session.restartPolling()
      }
      return
  }
}

Here, you implement the method that you’ll call when your session detects that you scanned a tag.

Typically, you would expect users to have only one tag close enough to their phone, but you should account for multiple tags in close proximity. If you detect this, you’ll stop the scan and alert the user. After showing the message, you’ll restart the reading session and let your user try again.