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()