Writing a URL or deep link into an NFC tag enables seamless integration between the physical and digital worlds. It offers instant access to online content and enhanced user experiences. Additionally, it creates automation opportunities, simplifying interactions such as opening web pages, accessing app-specific features, or triggering IoT actions. These capabilities make NFC tags valuable for marketing, smart environments, and personalization. This technology finds applications in retail, events, tourism, and healthcare, bringing convenience, innovation, and a modern touch.

In this post, we will continue evolving the app created in the “Harnessing NFC Technology in Your iOS App” post by adding two more functionalities: one for storing a regular web URL and another for adding a deep link to open the same app. By the end of this guide, you’ll be equipped to expand your app’s NFC capabilities and create an even more seamless user experience.

Storing web url into NFC tag

Add a new function to handle the write URL operation:
    func startWritingURL() async {
        nfcOperation = .writeURL
        startSesstion()
    }
    
    private func startSesstion() {
        nfcSession = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
        nfcSession?.begin()
    }
We ran out of Boolean operations, so I created an enum to implement the three current NFC operations: read, write, and write URL. For this process, we set the operation to perform and initiate an NFC session.
The readerSession delegate function handles connecting to the NFC tag and querying its status.
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
        guard let tag = tags.first else { return }
        
        session.connect(to: tag) { error in
            if let error = error {
                session.invalidate(errorMessage: "Connection error: \(error.localizedDescription)")
                return
            }
            
            tag.queryNDEFStatus { status, capacity, error in
                guard error == nil else {
                    session.invalidate(errorMessage: "Error checking NDEF status")
                    return
                }
                
                switch status {
                case .notSupported:
                    session.invalidate(errorMessage: "Not compatible tat")
                case  .readOnly:
                    session.invalidate(errorMessage: "Tag is read-only")
                case .readWrite:
                    switch self.nfcOperation {
                    case .read:
                        self.read(session: session, tag: tag)
                    case .write:
                        self.write(session: session, tag: tag)
                    case .writeURL:
                        self.writeUrl(session: session, tag: tag)
                    }
                    
                @unknown default:
                    session.invalidate(errorMessage: "Unknown NDEF status")
                }
            }
        }
    }
When a writable NFC tag is detected and the operation is set to .writeURL, the method responsible for writing the URL to the tag will be called.
    private func writeUrl(session: NFCNDEFReaderSession, tag: NFCNDEFTag) {
        guard let url = URL(string: "https://javios.eu/portfolio/"),
            let payload = NFCNDEFPayload.wellKnownTypeURIPayload(string: url.absoluteString) else {
            session.invalidate(errorMessage: "No se pudo crear el payload NDEF.")
            return
        }

        write(session, tag, payload) { error in
            guard  error == nil else { return }
            print(">>> Write: \(url.absoluteString)")
        }
    }
    
    private func write(_ session: NFCNDEFReaderSession,
                       _ tag: NFCNDEFTag,
                       _ nfcNdefPayload: NFCNDEFPayload, completion: @escaping ((Error?) -> Void)) {
        
        let NDEFMessage = NFCNDEFMessage(records: [nfcNdefPayload])
        tag.writeNDEF(NDEFMessage) { error in
            if let error = error {
                session.invalidate(errorMessage: "Writing error: \(error.localizedDescription)")
                completion(error)
            } else {
                session.alertMessage = "Writing succeeded"
                session.invalidate()
                completion(nil)
            }
        }
    }
This Swift code facilitates writing a URL as an NFC NDEF payload onto an NFC tag. The writeUrl function generates an NDEF payload containing a well-known type URI record that points to the URL “https://javios.eu/portfolio/“. If the payload is valid, the function invokes the write method, passing the NFC session, tag, and payload as parameters. The write function then creates an NFC NDEF message containing the payload and writes it to the NFC tag.
Once the URL is placed within the tag, you can use the tag to open the web link, functioning similarly to scanning a QR code that redirects you to a website.

Deeplinks

A deeplink in iOS is a type of link that directs users to a specific location within an app, rather than just opening the app’s home screen. This helps enhance the user experience by providing a direct path to particular content or features within the app.

In this example, we will create a deeplink that will open our current NFCApp directly:

When iOS detects the ‘nfcreader://jca.nfcreader.open’ deep link, it will open the currently post development app on iOS.

@main
struct NFCAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                handleDeeplink(url: url)
            }
        }
    }

    func handleDeeplink(url: URL) {
        // Maneja el deeplink aquí
        print("Se abrió la app con el URL: \(url)")
    }
}

By adding the .onOpenURL modifier, the app will be able to detect when it is launched (or awakened) via a deep link.

Finally, implement the deep link writing functionality by adapting the previously created writeUrl method:

    private func writeUrl(session: NFCNDEFReaderSession, tag: NFCNDEFTag, urlString: String) {
        guard let url = URL(string: urlString),
            let payload = NFCNDEFPayload.wellKnownTypeURIPayload(string: url.absoluteString) else {
            session.invalidate(errorMessage: "No se pudo crear el payload NDEF.")
            return
        }

        write(session, tag, payload) { error in
            guard  error == nil else { return }
            print(">>> Write: \(url.absoluteString)")
        }
    }

It would be called in the following way for creating deeplink

                case .readWrite:
                    switch self.nfcOperation {
                    case .read:
                        self.read(session: session, tag: tag)
                    case .write:
                        self.write(session: session, tag: tag)
                    case .writeURL:
                        self.writeUrl(session: session, tag: tag, urlString: "https://javios.eu/portfolio/")
                    case .writeDeeplink:
                        self.writeUrl(session: session, tag: tag, urlString: "nfcreader://jca.nfcreader.open")
                    }
                    

Deploy project on a real device for validating behaviour

Once the tag is written, when the deep link is triggered, the app will be closed and then reopened. You will be prompted to open the app again.

Conclusions

In this post, I have extended the functionalities we can implement with NFC tags by using URLs. You can find source code used for writing this post in following repository.

References

Copyright © 2024-2025 JaviOS. All rights reserved