Skip to content

Synchronizing Tapkey NFC Transponders

This chapter describes how to synchronize Tapkey NFC Transponders using the Tapkey Mobile SDK for iOS.

Only Tapkey NFC Transponders supported

Only NFC Transponder produced by Tapkey are supported. Empty NFC Transponder or those produced for a different purpose are not supported.

This documentation explains how to use the Tapkey Mobile SDK for iOS to synchronize Tapkey NFC Transponders, but does not discuss the basics of using NFC on iOS.

For more details about NFC and iOS, please refer to the Apple Core NFC Documentation.

General Concepts of Tapkey NFC Transponders

Tapkey NFC Transponders are individually encrypted and can only be read and written by the Tapkey Trust Service. Direct read/write access by smartphones or other parties is not possible. Therefore, in order to write data to a Tapkey NFC Transponder, an online connection between the server and the card is established. The mobile app acts as a relay between both by using the Tapkey Mobile SDK for iOS.

sequenceDiagram participant CARD as Tapkey NFC Transponder participant APP as Client App participant TTS as Tapkey Trust Service APP -->> APP: Wait for Tapkey NFC Transponder CARD -->> APP: Tapkey NFC Transponder presented APP ->>+ TTS: Establish connection Note over TTS, CARD: Tapkey NFC Transponder is written by the Tapkey Trust Service loop Synchronization TTS -->> CARD: Send Data CARD -->> TTS: Receive Data end TTS ->>- APP: Connection Closed CARD -->> APP:Tapkey NFC Transponder removed

Managing Grants

Tapkey NFC Transponders are encrypted, and therefore grants cannot be directly written to Tapkey NFC Transponders using the SDK. Instead, grants are managed via the Tapkey Web API. After grants have been modified, affected Tapkey NFC Transponders need to be synchronized in order to apply the changes.

Setup Requirements

To utilize the NFC API of iOS, the project needs to be configured to support specific NFC tags.

Enable NFC Capability

To enable the NFC API, add the Near Field Communication Tag Reading capability in the "Signing & Capabilities" section of your project.

Configure ReaderSession Formats

In the entitlements dictionary of your project, add the key com.apple.developer.nfc.readersession.formats with the value TAG to enable the usage of NFCTagReaderSession, which is necessary for reading and writing NFC tags.

<plist version="1.0">
<dict>
    ...
    <key>com.apple.developer.nfc.readersession.formats</key>
    <array>
        <string>TAG</string>
    </array>
</dict>
</plist>

Configure ISO 7816 IDs

In the Info.plist file of your application, add the key com.apple.developer.nfc.readersession.iso7816.select-identifiers with an array that includes the identifier D2760000850101 to retrieve the NfcTags as NFCISO7816Tag.

<plist version="1.0">
<dict>
    ...
    <key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
    <array>
        <string>D2760000850101</string>
    </array>
</dict>
</plist>

Start Listening for Tapkey NFC Transponders

To begin listening for NFC tags, create a custom implementation of the NFCTagReaderSessionDelegate and pass it to a new instance of NFCTagReaderSession.

For the pollingOption parameter, use iso14443.

// Create a new instance of NFCTagReaderSessionDelegate
let delegate: NFCTagReaderSessionDelegate = NFCTagReaderSessionDelegateImpl(...)

// Create a new NFCTagReaderSession with .iso14443 as pollingOption
let tagReaderSession = NFCTagReaderSession(pollingOption: .iso14443, delegate: delegate, queue: OperationQueue.current?.underlyingQueue)!

tagReaderSession.alertMessage = "Present Transponder"

// Start the reader session
tagReaderSession.begin()

The NFCTagReaderSession will notify the NFCTagReaderSessionDelegate when an NFC tag is detected.

Stop Listening for Tapkey NFC Transponders

After the synchronization process is complete, use the invalidate method or invalidate(errorMessage: String) method of the NFCTagReaderSession instance to stop listening.

// Close the session without any message
tagReaderSession.invalidate()

// or close the session with an error message
tagReaderSession.invalidate("Error Message")

Begin NFC Session

Synchronizing a Tapkey NFC transponder can take several seconds. As it happens quite frequently that the connection between the smartphone and the NFC transponder is lost temporarily while the synchronization is ongoing due to factors like tag movement, the Tapkey Mobile SDK provides functionality to continue the process, as soon as the NFC connection is reestablished. This is achieved by using the TKMRecoverableIsoDepConnection interface.

First, retrieve the TKMCardManager from the Tapkey TKMServiceFactory. Then, create an TKMIsoDepConnection from the detected NFC tag using the TKMNfcUtils.getIsoDepConnection(tag) method. If the returned TKMIsoDepConnection is nil, it indicates that the NFC tag technology is not supported.

== "Swift"

class NFCTagReaderSessionDelegateImpl: NSObject, NFCTagReaderSessionDelegate {

    var recoverableIsoDepConnection: TKMRecoverableIsoDepConnection? = nil

    ...

    public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {

            let cardManager = tapkeyServiceFactory.cardManager

            guard !tags.isEmpty else {
                session.invalidate(errorMessage: "No tags found")
                return
            }

            guard tags.count == 1 else {
                session.invalidate(errorMessage: "Too many tags")
                return
            }

            let tag = tags[0]

            // If tag is not `iso7816`, it is not a Tapkey NFC Card
            guard case .iso7816(let iso7816Tag) = tag else {
                session.invalidate(errorMessage: "Invalid Tag")
                return
            }

            // If `TKMNfcUtils.getIsoDepConnection(...)` returns nil, it is not a Tapkey NFC Card
            guard let isoDepConnection = TKMNfcUtils.getIsoDepConnection(readerSession: session, nfcTag: tag) else {
                session.invalidate(errorMessage: "Invalid Tag")
                return
            }

            if recoverableIsoDepConnection == nil {

                self.recoverableIsoDepConnection = TKMNfcUtils.createRecoverableIsoDepConnection(iso7816TagIdentifier: iso7816Tag.identifier)

                _ = recoverableIsoDepConnection.onStateChanged.addObserver { newState in
                    switch newState {

                    // When the tag is lost, notify the user and restart the polling
                    case .waitingForTag:
                        session.alertMessage = "Present Transponder"
                        session.restartPolling()
                        break

                    case .normal:
                        session.alertMessage = "Syncing"
                        break

                    case .completed:
                        session.alertMessage = "Completed"
                        break

                    // When a newly detected card is different, notify the user and restart the polling
                    case .wrongTag:
                        session.alertMessage = "Wrong Transponder"
                        session.restartPolling()
                        break

                    @unknown default:
                        break
                    }
                }

                // Pass the TKMRecoverableIsoDepConnection instance to the methods of `CardManager` to perform actions on the card
                cardManager.syncCardAsync(userId: userId, uid: tagIdentifier, isoDepConnection: recoverableIsoDepConnection, ....)
                    ...
            }

            // Whenever a tag is discovered, pass it to the `TKMRecoverableIsoDepConnection` to start or continue the connection
            recoverableIsoDepConnection.replaceConnection(uid: iso7816Tag.identifier, newConnection: isoDepConnection)
    }
}

The RecoverableIsoDepConnection instance, recoverableIsoDepConnection, is used to manage the NFC connection and handle potential interruptions. By subscribing to the state observable, you can provide appropriate user guidance based on the different states of the synchronization process.

Ensure that you pass the TKMRecoverableIsoDepConnection to the relevant methods of the TKMCardManager for performing actions on the NFC card. The synchronization process can be initiated by calling cardManager.syncCardAsync(userId: userId, iso7816Tag.identifier, recoverableIsoDepConnection, ...).

Synchronize Tapkey NFC Transponders

To synchronize a Tapkey NFC Transponder, you can use the syncCardAsync method of the TKMCardManager and pass the previously created TKMRecoverableIsoDepConnection instance. The syncCardAsync method returns a Promise that resolves when the synchronization is successfully completed or rejects if the synchronization fails.

// The recoverableIsoDepConnection that was created earlier
var recoverableDataConnection: TKMRecoverableIsoDepConnection = ...

cardManager.syncCardAsync(
    userId: userId,
    uid: tagIdentifier,
    isoDepConnection: recoverableIsoDepConnection,
    progress: { (progress: Float?) in
        guard let progress = progress else {
            return
        }
        session.alertMessage = "Syncing \(progress)"
    },
    cancellationToken: TKMCancellationTokens.None)
    .continueOnUi { _ -> Void in
        // Display a success message to the user
        session.alertMessage = "Success"
        // Invalidate the NFCTagReaderSession
        session.invalidate()
    }
    .catchOnUi { (e: TKMAsyncError) -> Void? in
        // Invalidate the NFCTagReaderSession with an error message
        session.invalidate(errorMessage: "Failed")
        return nil
    }
    .finallyOnUi {
        // ToDo: Cleanup to avoid memory leaks
    }
    .conclude()

You can monitor the progress of the synchronization by providing a callback function that receives the progress updates. The progress parameter in the code snippet above represents the synchronization progress as a float value. You can use this information to update the user interface or provide feedback to the user during the synchronization process.

Register a Tapkey NFC Transponders

Before a grant can be assigned to a Tapkey NFC Transponder, the card needs to be registered to the owner's account. To accomplish this, you can use the takeCardOwnershipAsync method of the TKMCardManager class and provide the owner's account ID and the previously created TKMRecoverableIsoDepConnection instance.

Transponder must be unassigned

Please note that the Tapkey NFC Transponder must either be unassigned or not have any grants assigned to it. If the card is already assigned to another owner's account and has grants, the grants must be removed by the current owner before this operation can succeed.

// The recoverableIsoDepConnection that was created earlier
var recoverableDataConnection: TKMRecoverableIsoDepConnection = ...

cardManager.takeCardOwnershipAsync(
    userId: userId,
    uid: tagIdentifier,
    ownerId: ownerId,
    isoDepConnection: recoverableIsoDepConnection,
    progress: { (progress: Float?) in
        guard let progress = progress else {
            return
        }
        session.alertMessage = "Syncing \(progress)"
    },
    cancellationToken: TKMCancellationTokens.None)
    .continueOnUi { _ -> Void in
        // Display a success message to the user
        session.alertMessage = "Success"
        // Invalidate the NFCTagReaderSession
        session.invalidate()
    }
    .catchOnUi { (e: TKMAsyncError) -> Void? in
        // Invalidate the NFCTagReaderSession with an error message
        session.invalidate(errorMessage: "Failed")
        return nil
    }
    .finallyOnUi {
        // ToDo: Cleanup to avoid memory leaks
    }
    .conclude()