
Near Field Communication (NFC) allows devices to communicate wirelessly over short distances, making it a useful technology for tasks such as payments, access control, and information sharing. In this guide, we’ll walk through the process of integrating Core NFC into an iOS application using Swift. We’ll cover the basics of setting up the project, reading NFC tags, and handling the data.
Setting Up the Project
- Open Xcode and Create a New Project:
- Select the “App” template.
- Name your project (e.g., “NFCReader”).
- Choose Swift as the language and SwiftUI as the user interface.
- Configure Privacy Settings:
- Open
Info.plist
. - Add the key
NFCReaderUsageDescription
with a description like “We need access to NFC to scan tags”.
- Open
Understanding Core NFC
Core NFC provides the capability to read Near Field Communication (NFC) tags of types 1 through 5 that contain data in NDEF (NFC Data Exchange Format) format. iOS apps using Core NFC can read NFC tags, but they cannot write to NFC tags or communicate with devices over NFC.
Designing the User Interface
- Open
ContentView.swift
and set up a basic UI with a button to start scanning for NFC tags and a text field to display the scanned data.
import SwiftUI
struct ContentView: View {
@State private var isShowingScanner = false
@State private var scannedData = ""
var body: some View {
VStack {
Text("Scanned NFC Data:")
.font(.headline)
Text(scannedData)
.font(.largeTitle)
.padding()
Button(action: {
isShowingScanner = true
}) {
Text("Start Scanning")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.sheet(isPresented: $isShowingScanner) {
NFCScannerView(scannedData: $scannedData)
}
}
}
Creating the NFC Scanner View
- Create a new Swift file named
NFCScannerView.swift
. - Import the necessary frameworks and set up the UIViewControllerRepresentable to bridge UIKit with SwiftUI.
import SwiftUI
import CoreNFC
struct NFCScannerView: UIViewControllerRepresentable {
@Binding var scannedData: String
func makeCoordinator() -> Coordinator {
Coordinator(scannedData: $scannedData)
}
func makeUIViewController(context: Context) -> UIViewController {
let viewController = NFCScannerViewController()
viewController.delegate = context.coordinator
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }
class Coordinator: NSObject, NFCNDEFReaderSessionDelegate {
@Binding var scannedData: String
init(scannedData: Binding<String>) {
_scannedData = scannedData
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// Handle errors
print("NFC Session invalidated: \(error.localizedDescription)")
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for record in message.records {
if let string = String(data: record.payload, encoding: .utf8) {
DispatchQueue.main.async {
self.scannedData = string
}
}
}
}
}
}
}
Implementing the NFC Scanner View Controller
- Create a new Swift file named
NFCScannerViewController.swift
. - Implement the NFC scanning logic using Core NFC.
import UIKit
import CoreNFC
class NFCScannerViewController: UIViewController {
var nfcSession: NFCNDEFReaderSession?
var delegate: NFCNDEFReaderSessionDelegate?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
startScanning()
}
func startScanning() {
guard NFCNDEFReaderSession.readingAvailable else {
print("NFC is not available on this device.")
return
}
nfcSession = NFCNDEFReaderSession(delegate: delegate, queue: nil, invalidateAfterFirstRead: false)
nfcSession?.alertMessage = "Hold your iPhone near the NFC tag to scan."
nfcSession?.begin()
}
}
Explanation
- ContentView: This SwiftUI view displays a button that starts the NFC scanning process and a text field to show the scanned data.
- NFCScannerView: This struct represents a view that uses
UIViewControllerRepresentable
to present a UIKit view controller in SwiftUI. It initializes an NFC reader session and handles detected NDEF messages. - NFCScannerViewController: This class manages the NFC scanning session. It starts the NFC reader session and ensures that it can read NDEF messages.
Running the App
- Build and run the app on a physical device (the simulator does not support NFC).
- Tap the “Start Scanning” button and hold the iPhone near an NFC tag.
- The scanned data should appear on the screen.
Enhancing the App
To make this app more robust and feature-rich, consider adding:
- Error handling for NFC operations, including session invalidation and reading errors.
- UI updates to indicate scanning status (e.g., a loading spinner).
- Parsing NDEF records to extract specific types of data (e.g., URLs, text records).
- Writing data to NFC tags (if iOS support for writing is added in the future).
Detailed Explanation
Setting Up Core NFC
Core NFC requires specific setup in your Xcode project and a basic understanding of how NFC works. Core NFC is limited to reading NDEF tags, which is sufficient for many applications, such as scanning product tags, accessing information, and initiating actions based on tag data.
Initializing the NFC Reader Session
An NFC reader session is initiated by creating an NFCNDEFReaderSession
object. This session is responsible for handling the detection of NFC tags and reading their contents. When the session begins, the user is prompted to hold their device near an NFC tag.
nfcSession = NFCNDEFReaderSession(delegate: delegate, queue: nil, invalidateAfterFirstRead: false)
nfcSession?.alertMessage = "Hold your iPhone near the NFC tag to scan."
nfcSession?.begin()
invalidateAfterFirstRead
: If set totrue
, the session ends after the first tag is read. For continuous reading, set it tofalse
.
Handling NDEF Messages
When an NFC tag is detected, the readerSession(_:didDetectNDEFs:)
method is called. This method provides an array of NFCNDEFMessage
objects, each containing NFCNDEFRecord
objects that represent the data stored on the tag.
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for record in message.records {
if let string = String(data: record.payload, encoding: .utf8) {
DispatchQueue.main.async {
self.scannedData = string
}
}
}
}
}
Error Handling
Proper error handling ensures a smooth user experience. The readerSession(_:didInvalidateWithError:)
method handles session invalidation errors, such as when the session times out or the user cancels it.
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// Handle errors
print("NFC Session invalidated: \(error.localizedDescription)")
}
SwiftUI Integration
Using UIViewControllerRepresentable
, you can seamlessly integrate UIKit-based NFC functionality into a SwiftUI app. This approach allows you to leverage existing UIKit code while building modern SwiftUI interfaces.
struct NFCScannerView: UIViewControllerRepresentable {
@Binding var scannedData: String
func makeCoordinator() -> Coordinator {
Coordinator(scannedData: $scannedData)
}
func makeUIViewController(context: Context) -> UIViewController {
let viewController = NFCScannerViewController()
viewController.delegate = context.coordinator
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }
class Coordinator: NSObject, NFCNDEFReaderSessionDelegate {
@Binding var scannedData: String
init(scannedData: Binding<String>) {
_scannedData = scannedData
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// Handle errors
print("NFC Session invalidated: \(error.localizedDescription)")
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for record in message.records {
if let string = String(data: record.payload, encoding: .utf8) {
DispatchQueue.main.async {
self.scannedData = string
}
}
}
}
}
}
}
In this comprehensive guide, we’ve built a basic NFC reader app using Core NFC in Swift. This app serves as a foundation for more advanced applications that involve reading NFC tags for various purposes.