Core NFC Tutorial for NFC on iOS Devices

With the release of iOS 11, for the first time third-party developers are able to use the NFC reader on devices iPhone 7 and higher. This could be used for passing along identification information and a whole host of other data exchange applications from door locks to subway passes.

The technology used on iOS 11 is called Core NFC, and I’m going to go over how to use it in this tutorial in Swift 4.

Core NFC Device communications on iOS

Because Core NFC is currently read-only, functionality such as contactless payments will not be possible out of the box, there are however other applications we can use the Core NFC reading capabilities for. So let me show you how it works.

The first step to work with NFC is to enable it for your App ID in the Apple Developer Center. Create a new App ID and enable “NFC Tag Reading” as a capability for the app.

NFC Tag Reading App Service

After you do this, I recommend creating a development/distribution provisioning profile specifically for this app ID, so that the NFC reading capability will be present when you try to build.

Next, in your Xcode project add the entitlement to your projectName.entitlements file. You’ll need to right click the file and select “Open As Source Code” to manually enter this key as shown:

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
  <string>NDEF</string>
</array>

If you do not have an entitlements file, you can manually create one and point to it in the settings of the project. Under “Build Settings” look for the record “Code Signing Entitlements”, and punch in the relative path to your entitlements file. In my case it’s “CoreNFC-Tutorial/CoreNFC-Tutorial.entitlements” because my project files are inside a sub-folder called “CoreNFC-Tutorial”.

Next, we need to add the usage string to your Xcode project. Open your Info.plist file and add a new record, start typing and allow it to autocomplete “Privacy – NFC Scan Usage Description”. This message will be shown to users when NFC is used, so for the value enter something useful for the user such as “NFC is needed to unlock doors.”.

Next, in our code we want to import the CoreNFC module.

import CoreNFC

Note: Core NFC is completely unavailable on the iOS simulator, and even importing the module will fail. So Core NFC is for device-only testing!

I created an NFCHelper.swift to store all my NFC-related calls, and put everything in an NFCHelper class. In the init method of the class I created a session. Core NFC requires you utilize the NFCNDEFReaderSession class in order to start listening for NFC communications. (Note the class NFCReaderSession is abstract and should not be used directly)

class NFCHelper {
  init() {
    let session =
      NFCNDEFReaderSession(delegate: self,
                           queue: nil,
                           invalidateAfterFirstRead: true)
    session.begin()
  }
}

Here we create the session, and pass in a nil dispatch queue. Doing so will cause NFCNDEFReaderSession to automatically create a serial dispatch queue.

Creating a session, we can specify a delegate in our NFCNDEFReaderSession class. I would like to use the NFCHelper class as the delegate, so we must first adhere to the delegate protocol, NFCNDEFReaderSessionDelegate. This delegate is based on an Objective-C object, so we must first also adhere to NSObject. NFCNDEFReaderSessionDelegate has two delegate methods we must implement:

func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error)
 
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage])

These two callbacks are called when an NFC session has a validation error, or when NFC activity is detected. How we use the messages will depend on your specific application, but everything you need to know will be sent through the didDetectNDEFs callback in the form of records inside of messages. To get started, you can log the results of each message in a loop. These are NFCNDEFPayload objects, which contain an identifier, payload, type, and typeNameFormat.

func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
  print("Did detect NDEFs.")
  // Loop through all messages
  for message in messages {
    for record in message.records {
      print(record.identifier)
      print(record.payload)
      print(record.type)
      print(record.typeNameFormat)
    }
  }
}

To clean this up a little bit so I can actually integrate with the front-end of the app, I created a callback specific for my application. You may want to do something similar. I added a callback variable that the implementing view can work with:

I call this when I get a payload from an NFC tag, or an error:

class NFCHelper: NSObject, NFCNDEFReaderSessionDelegate {
  ...
  var onNFCResult: ((Bool, String) -> ())?
  ...
}

I also broke out my init function to open up a session using a method, so I can restart the session from a button on my ViewController. My final code for my NFCHelper.swift file is as follows:

//
//  NFCHelper.swift
//  CoreNFC-Tutorial
//
//  Created by Jameson Quave on 6/6/17.
//  Copyright © 2017 Jameson Quave. All rights reserved.
//
 
import Foundation
import CoreNFC
 
class NFCHelper: NSObject, NFCNDEFReaderSessionDelegate {
 
  var onNFCResult: ((Bool, String) -> ())?
 
  func restartSession() {
    let session =
    NFCNDEFReaderSession(delegate: self,
                       queue: nil,
                       invalidateAfterFirstRead: true)
    session.begin()
  }
  // MARK: NFCNDEFReaderSessionDelegate
  func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
    guard let onNFCResult = onNFCResult else {
      return
    }
    onNFCResult(false, error.localizedDescription)
  }
  func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
    guard let onNFCResult = onNFCResult else {
      return
    }
    for message in messages {
      for record in message.records {
        if(record.payload.count > 0) {
          if let payloadString = String.init(data: record.payload, encoding: .utf8) {
              onNFCResult(true, payloadString)
          }
        }
      }
    }
  }
 
}

I also set up a simple UI in my ViewController to demonstrate usage of this class:

//
//  ViewController.swift
//  CoreNFC-Tutorial
//
//  Created by Jameson Quave on 6/6/17.
//  Copyright © 2017 Jameson Quave. All rights reserved.
//
 
import UIKit
 
class ViewController: UIViewController {
  var helper: NFCHelper?
  var payloadLabel: UILabel!
  var payloadText = ""
  override func viewDidLoad() {
    super.viewDidLoad()
    // Add a detect button
    let button = UIButton(type: .system)
    button.setTitle("Read NFC", for: .normal)
    button.titleLabel?.font = UIFont(name: "Helvetica", size: 28.0)
    button.isEnabled = true
    button.addTarget(self, action: #selector(didTapReadNFC), for: .touchUpInside)
    button.frame = CGRect(x: 60, y: 200, width: self.view.bounds.width - 120, height: 80)
    self.view.addSubview(button)
    // Add a label to display the payload in
    payloadLabel = UILabel(frame: button.frame.offsetBy(dx: 0, dy: 220))
    payloadLabel.text = "Press Read to see payload data."
    payloadLabel.numberOfLines = 100
    self.view.addSubview(payloadLabel)
  }
  // Called by NFCHelper when a tag is read, or fails to read
  func onNFCResult(success: Bool, message: String) {
    if success {
      payloadText = "\(payloadText)\n\(message)"
    }
    else {
      payloadText = "\(payloadText)\n\(message)"
    }
    // Update UI on main thread
    DispatchQueue.main.async {
      self.payloadLabel.text = self.payloadText
    }
 
  }
  // Called when user taps Read NFC Button
  @objc func didTapReadNFC() {
    if helper == nil {
      helper = NFCHelper()
      helper?.onNFCResult = self.onNFCResult(success:message:)
    }
    payloadText = ""
    helper?.restartSession()
  }
}

From here you can integrate the rest of your app with your intended use-case. Whether it’s to identify visitors to an event, check out the stats on your Amiibo, or even process payments, the Core NFC API from Apple has finally opened up the possibilities for NFC integration on these new devices. What kind of NFC product are you working on? Let me know at jquave@gmail.com.

Full Source Code

Video Tutorial (YouTube)


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.

Subscribe via RSS