Write NFC Tag with SwiftUI

In the previous post, we learned how to read an NFC tag. In this one, we’ll explore how to write to an NFC tag. As I mentioned in the previous post: ‘Once you create a project, you need to add the Near Field Communication capability. In your code, you must also import CoreNFC. After that, add the “Privacy – NFC Scan Usage Description” permission in the Info.plist file. If this is omitted, your app will crash, and you’ll be left confused about the reason.’

We want to write both text and a URL to the tag, so in ContentView we have:

import SwiftUI
import CoreNFC

struct ContentView: View {
    @State var nfcWriter = NFCWriter()
    @State var textToWrite = ""
    @State var urlToWrite = ""
    
    var body: some View {
        VStack(spacing: 10) {
            TextField("Text to write", text: $textToWrite)
            TextField("Url to write", text: $urlToWrite)
            Button("Write NFC") {
                nfcWriter.write(url: urlToWrite, text: textToWrite)
            }.padding()
        }.padding()
    }
}

We have two TextField components to capture user input and a button that calls the write function.

Now, let’s look at the main component, the writer. Create a file named NFCWriter and copy the following code into it:

import Foundation
import CoreNFC

@Observable
public class NFCWriter: NSObject, NFCNDEFReaderSessionDelegate {
    var startAlert = "Hold your iPhone near the tag."
    var session: NFCNDEFReaderSession?
    var urlToWrite = "https://www.nicoladefilippo.com"
    var textToWrite = "Hello World"
    
    public func write(url: String, text: String) {
        guard NFCNDEFReaderSession.readingAvailable else {
            print("Error")
            return
        }
        self.urlToWrite = url
        self.textToWrite = text
        session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
        session?.alertMessage = self.startAlert
        session?.begin()
    }
    
    public func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
        // Read logic
    }
    
    public func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {

        guard tags.count == 1 else {
            session.invalidate(errorMessage: "Cannot Write More Than One Tag in NFC")
            return
        }
        let currentTag = tags.first!
        
        session.connect(to: currentTag) { error in
            
            guard error == nil else {
                session.invalidate(errorMessage: "cound not connect to NFC card")
                return
            }
            
            currentTag.queryNDEFStatus { status, capacity, error in
                
                guard error == nil else {
                    session.invalidate(errorMessage: "Write error")
                    return
                }
                
                switch status {
                    case .notSupported: 
                        session.invalidate(errorMessage: "Not Suported")
                    case .readOnly:
                        session.invalidate(errorMessage: "ReadOnly")
                    case .readWrite:
                    
                        let textPayload = NFCNDEFPayload.wellKnownTypeTextPayload(
                            string: self.textToWrite,
                            locale: Locale.init(identifier: "en")
                        )!
                        
                        let uriPayload = NFCNDEFPayload.wellKnownTypeURIPayload(
                            url: URL(string: self.urlToWrite)!
                        )!
                        
                        let messge = NFCNDEFMessage.init(
                            records: [
                                uriPayload,
                                textPayload
                            ]
                        )
                        currentTag.writeNDEF(messge) { error in
                            
                            if error != nil {
                                session.invalidate(errorMessage: "Fail to write nfc card")
                            } else {
                                session.alertMessage = "Successfully writtern"
                                session.invalidate()
                            }
                        }
                    
                    @unknown default:
                        session.invalidate(errorMessage: "unknown error")
                }
            }
        }
    }
    
    public func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
    }
    
    public func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        print("Session did invalidate with error: \(error)")
        self.session = nil
    }
}

In the write function, we initialize the session and the data we want to write. Note that in this case, invalidateAfterFirstRead is set to false because it can’t be true when writing (otherwise, the session would immediately be invalidated).

The core of the functionality is the public func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) function.

In this function, we check that only one tag is near the phone (since only one tag can be written to), then retrieve the tag and establish a connection to it. If everything checks out, we query it to verify its status. If writing is possible, we add an array of records containing a text and a URL (note how records are initialized differently based on the type). The capacity property indicates the maximum NDEF message size, in bytes, that can be stored on the tag.

You should now be able to write to your tag.

The code https://github.com/niqt/WriteNfcTag

To subscribe to my newsletter [https://nicoladefilippo.com/#mailinglist]

Note: English is not my native language, so I apologize for any errors. I use AI solely to generate the banner of the post; the content is human-generated.

share this post with friends

Picture of Nicola De filippo

Nicola De filippo

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve

Leave a comment

Your email address will not be published. Required fields are marked *

Who I am

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve.

Follow Me Here

Get The Latest Updates

Periodically receive my super contents on coding and programming

join the family;)

Recent Posts