Go to top button

How SimpleNFC Leverages CoreNFC and CoreData for NFC Operations and Data Persistence

Alex Huang

May 31

Estimated reading time: 13 minute(s)

How SimpleNFC Leverages CoreNFC and CoreData for NFC Operations and Data Persistence - Splash image

Summary


In this comprehensive guide, we delve into the SimpleNFC app's use of CoreNFC for NFC scanning and CoreData for data persistence. Discover the app's primary goals, essential frameworks, and detailed code snippets demonstrating how to implement NFC reading and writing, display scan results, and save NFC data. Additionally, learn about potential future enhancements with CloudKit. This guide provides valuable insights for iOS developers looking to integrate NFC functionality and data persistence into their apps.

Learn more about this project
GitHub

---

Download SimpleNFC Utilities: App Store

Introduction


In this guide, we'll explore how the SimpleNFC app leverages CoreNFC for NFC operations and CoreData for data persistence. We'll dive into the key functionalities, provide code snippets, and explain how different components interact within the app.

Thought Process and Base Functionality



When building SimpleNFC, my primary goals were to:

1. Enable NFC scanning.
2. Display the results of the scans.
3. Allow replaying the scanned NFC data.
4. Provide an option to save the NFC data.
5. Potentially add functionality for sharing scan results (as a stretch goal).

Frameworks Needed



To achieve this functionality, the following frameworks were used:



NFC Scanning



Using CoreNFC to enable scanning of NFC tags.

Key Code Snippet: HomeViewModel.swift - NFC Reading

import CoreNFC
import SwiftUI

final class HomeViewModel: NSObject, ObservableObject {
var session: NFCNDEFReaderSession?

func beginScanning() {
guard NFCNDEFReaderSession.readingAvailable else {
// Handle devices that don't support NFC reading
return
}
session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
session?.alertMessage = "Hold your iPhone near the item to learn more about it."
session?.begin()
}

func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
// Handle detected NFC tags
if let tag = tags.first {
session.connect(to: tag) { error in
if let error = error {
// Handle connection error
return
}
tag.queryNDEFStatus { status, capacity, error in
// Handle NDEF status and read data
}
}
}
}

func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// Handle session invalidation
}
}


For a thorough introduction to CoreNFC, including how to read and write NFC tags and handle various NFC protocols, check out the guide on Enhancing iOS Apps with Core NFC: A Comprehensive Guide. It's an essential resource for understanding the capabilities and implementation of CoreNFC in your iOS applications.

Displaying the Results



Showing the results of NFC scans and providing options to save or discard the data.

Key Code Snippet: CurrentNDEFMessage.swift - Displaying NFC Data

import CoreNFC
import Foundation

final class CurrentNDEFMessage: ObservableObject {
@Published var ndefMessage: NFCNDEFMessage?
@Published var nfcData: NFCData?
@Published var editUUID: UUID?

func setNDEFMessageFromDB(item: NFCData) {
guard let recordType = item.record_type,
let identifier = item.identifier,
let payload = item.payload,
let format = NFCTypeNameFormat(rawValue: UInt8(item.format)) else {
return
}

if format == .absoluteURI || String(data: recordType, encoding: .utf8) == "U" {
ndefMessage = NFCNDEFMessage(records: [NFCNDEFPayload.wellKnownTypeURIPayload(string: String(data: payload, encoding: .utf8)!)!])
} else if String(data: recordType, encoding: .utf8) == "T" {
ndefMessage = NFCNDEFMessage(records: [NFCNDEFPayload.wellKnownTypeTextPayload(string: String(data: payload, encoding: .utf8)!, locale: .current)!])
} else {
ndefMessage = NFCNDEFMessage(records: [NFCNDEFPayload(format: format, type: recordType, identifier: identifier, payload: payload)])
}

ndefMessage?.records[0].identifier = identifier
nfcData = item
}

func setNewNFCNDEFMessage() {
ndefMessage = NFCNDEFMessage(records: [NFCNDEFPayload(format: .nfcWellKnown, type: "T".data(using: .utf8)!, identifier: Data(), payload: Data())])
}
}


Saving the Data



Persisting the scanned NFC data using CoreData.

Key Code Snippet: HomeViewModel.swift - Saving Data

import CoreData

final class HomeViewModel: NSObject, ObservableObject {
@Published var items: [NFCData] = []

func fetchItems(context: NSManagedObjectContext) {
let request: NSFetchRequest<NFCData> = NFCData.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \NFCData.timestamp, ascending: false)]
do {
items = try context.fetch(request)
} catch {
// Handle fetch error
}
}

func saveItem(context: NSManagedObjectContext, data: NFCData) {
do {
try context.save()
} catch {
// Handle save error
}
}
}


CoreData: Persistence



Setting up CoreData involves initializing an NSPersistentContainer and loading the persistent stores.

Key Code Snippet: Persistence.swift

import CoreData

struct PersistenceController {
static let shared = PersistenceController()

let container: NSPersistentContainer

init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "SimpleNFC")
if inMemory {
container.persistentStoreDescriptions.first?.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores { description, error in
if let error = error as NSError? {
// Handle error
fatalError("Unresolved error \(error), \(error.userInfo)")
}
}
}
}


For a deeper understanding of CoreData, including its components, features, and implementation details, check out Core Data: A Comprehensive Guide for iOS Developers. This guide helps developers manage their app's data model, persistence, and state management effectively.

Managing NFC Data



We define an entity NFCData to store NFC message details.

Defining NFCData Entity



The NFCData entity includes attributes such as id, identifier, record_type, payload, timestamp, and more. Refer to the NFCData Entity for the detailed model.

Handling NFC Reader Session Delegate Methods



The NFC reading functionality relies on the NFCNDEFReaderSessionDelegate methods.

Key Code Snippet: HomeViewModel.swift - NFC Reader Session Delegate Methods

extension HomeViewModel: NFCNDEFReaderSessionDelegate {
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
if let tag = tags.first {
session.connect(to: tag) { error in
if let error = error {
// Handle connection error
return
}
tag.queryNDEFStatus { status, capacity, error in
switch status {
case .notSupported:
// Handle unsupported tags
break
case
.readWrite:
// Handle read-write tags
break
case
.readOnly:
// Handle read-only tags
break
case
.unknown:
// Handle unknown tags
break
@unknown default:
// Handle future cases
break
}
}
}
}
}

func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// Handle session invalidation
}
}


Creating and Managing NFC Data



To allow users to create new NFC data, we provide a screen where they can enter the necessary information and save it.

Key Code Snippet: DetailedNFCViewModel.swift - Creating NFC Data

import CoreData
import SwiftUI

final class DetailedNFCViewModel: ObservableObject {
@Published var record: NFCNDEFPayload?
@Published var identifier: Data = Data()
@Published var payloadTypeNameFormat: NFCTypeNameFormat = .unknown
@Published var recordType: Data = Data()
@Published var payload: Data = Data()
@Published var alertItem: AlertItem?
@Published var savedSuccessfully = false

var isValid: Bool {
guard !identifier.isEmpty else {
alertItem = AlertContext.nfcIdentiferInvalidated
return false
}
guard !payload.isEmpty else {
alertItem = AlertContext.nfcPayloadInvalidated
return false
}
return true
}

func saveChanges(context: NSManagedObjectContext, currentNDEFMessage: CurrentNDEFMessage) {
guard isValid else { return }
var item: NFCData
if let editUUID = currentNDEFMessage.editUUID, let nfcData = currentNDEFMessage.nfcData {
item = nfcData
item.last_accessed = Date()
} else {
item = NFCData(context: context)
item.id = UUID()
item.timestamp = Date()
item.last_accessed = Date()
}
item.identifier = self.identifier
item.format = Int16(self.payloadTypeNameFormat.rawValue)
item.record_type = self.recordType
item.payload = self.payload
do {
try context.save()
currentNDEFMessage.editUUID = nil
currentNDEFMessage.nfcData = nil
self.savedSuccessfully = true
} catch {
alertItem = AlertContext.failedToSave
}
}
}


View for Creating NFC Data



The view allows users to input NFC data and save it using the view model.

Key Code Snippet: DetailedNFCView.swift - Creating NFC Data

import SwiftUI
import CoreNFC

struct DetailedNFCView: View {
@Binding var isActive: Bool
@Environment(\.managedObjectContext) private var viewContext
@StateObject private var viewModel = DetailedNFCViewModel()
@EnvironmentObject var currentNDEFMessage: CurrentNDEFMessage

var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text("NFC Data")
.font(.title)
.padding(.bottom, 10)

if viewModel.record != nil {
Form {
Section(header: Text("Identification")) {
TextField("Payload Identifier", text: Binding(
get: { String(data: viewModel.identifier, encoding: .utf8) ?? "N/A" },
set: { newValue in viewModel.identifier = newValue.data(using: .utf8) ?? Data() }
))
}
Section(header: Text("Payload Type")) {
Picker("Format", selection: $viewModel.payloadTypeNameFormat) {
ForEach(typeNameFormats, id: \.self) { format in
Text(format.description)
}
}
.pickerStyle(MenuPickerStyle())

Picker("Type", selection: Binding(
get: { String(data: viewModel.recordType, encoding: .utf8) ?? "N/A" },
set: { newValue in viewModel.recordType = newValue.data(using: .utf8) ?? Data() }
)) {
ForEach(recordTypes) { type in
Text(type.description)
}
}
.pickerStyle(MenuPickerStyle())
}
Section(header: Text("Payload")) {
TextEditor(text: Binding(
get: { String(data: viewModel.payload, encoding: .utf8) ?? "N/A" },
set: { newValue in viewModel.payload = newValue.data(using: .utf8) ?? Data() }
))
.frame(height: 100)
}
Button {
viewModel.saveChanges(context: viewContext, currentNDEFMessage: currentNDEFMessage)
} label: {
Text("Save NFC Data")
}
}
.cornerRadius(10)
} else {
Text("No NFC record available.")
}
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(10)
.onAppear {
if let record = currentNDEFMessage.ndefMessage?.records.first {
viewModel.record = record
viewModel.identifier = record.identifier
viewModel.payloadTypeNameFormat = record.typeNameFormat
viewModel.recordType = record.type
viewModel.payload = record.payload
}
}
.alert(item: $viewModel.alertItem) { alertItem in
Alert(title: alertItem.title, message: alertItem.message, dismissButton: alertItem.dismissButton)
}
.onChange(of: viewModel.savedSuccessfully) { isSaved in
if isSaved { isActive = false }
}
}
}


Additional Resources



For further enhancement of your NFC and iOS app development skills, consider exploring the following resources:


This guide provides a thorough introduction to CoreNFC, including how to read and write NFC tags and handle various NFC protocols. It's an essential resource for understanding the capabilities and implementation of CoreNFC in your iOS applications.


CoreData is crucial for data persistence in iOS apps. This guide covers CoreData's components, features, and implementation details, helping developers manage their app's data model, persistence, and state management effectively.


Implementing the MVVM architecture in SwiftUI can enhance the maintainability and scalability of your app. This resource provides best practices and tips for leveraging MVVM with SwiftUI, ensuring your app remains well-organized and testable.


Understanding the broader applications of NFC, such as in the retail industry, can provide insights into how this technology can be leveraged for various use cases. This article explores the transformative impact of NFC and RFID in retail, highlighting innovative implementations and future prospects.


For developers new to iOS development, this guide introduces essential Apple frameworks, including SwiftUI, UIKit, CoreData, and more. It provides an overview of each framework, key features, and practical examples, offering a solid foundation for building robust, feature-rich applications.

Conclusion



By understanding these snippets and their roles within the SimpleNFC app, you can effectively learn how to implement NFC reading/writing, data persistence, and data sharing in your own iOS applications using CoreNFC and CoreData. For more detailed code and additional context, please refer to the full project on GitHub.

Let's Connect!

I'm always excited to discuss new opportunities, innovative projects, and collaboration possibilities. Feel free to reach out through email or connect with me on  LinkedIn.

Alex Huang's Logo

Alex Huang

© 2024 Alex Huang. All Rights Reserved.