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
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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:

  1. 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.
  2. 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.
  3. 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, the readLocation 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:

  1. Create a textual NFCNDEFPayload. As stated earlier, this is akin to an NDEF Record.
  2. Make a new NFCNDEFMessage with the payload so that you can save it to the NFC device.
  3. 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.

Note: This tutorial covers Well-Known and Unknown. To learn about the other types, check out the links listed at the end of this tutorial.

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:

Your app looking for an NFC tag to scan and write data to

Note: Remember, you’ll need to use a physical device and have an NFC tag that supports writing capabilities.

Once you place your phone on your NFC tag, you’ll see a message that your location was successfully saved.

The app successfully saving location data to an NFC tag

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.

  1. First, you initiate reading a tag. If it can be read, it will return any messages it finds.
  2. 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:

The app scanning and reading an NFC tag with location data

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:

  1. Call a completion handler and pass the message to it. This will be useful for chaining multiple NFC tasks together.
  2. Decode the payload so that you can parse the tag’s records. You’ll come back to this in a little bit.