Integrating the Tapkey Mobile SDK for iOS¶
Latest Version
The latest version of the Tapkey Mobile SDK for iOS is 2.7.2.0
Warning
This documentation refers to version 2.7.0.0
and later. This documentation may not be appropriate for older versions. Take a look at the upgrade section for upgrading to the latest version of the Tapkey Mobile SDK.
Sample App¶
A Tapkey Mobile SDK sample for iOS app is available on GitHub.
Requirements¶
The Tapkey Mobile SDK is written in Swift and is distributed as a compiled static Swift 5.1.3
library. The target App has to be built with Xcode 11
or later.
Adding the Tapkey Mobile SDK via CocoaPods¶
The Tapkey Mobile SDK is published as a Pod in a private Podspec repository. To tell CocoaPods where to find the artefacts add the Tapkey Podspec repository, add the official Tapkey Podspec repository as a source to the beginning of the Podfile and add TapkeyMobileLib
as a dependency to your target app. Also make sure, use_frameworks!
is set.
source 'https://github.com/tapkey/TapkeyCocoaPods'
source 'https://cdn.cocoapods.org/'
use_frameworks!
target 'App' do
pod 'TapkeyMobileLib', '2.7.2.0'
end
Bootstrap the Tapkey Mobile SDK¶
- Import the
TapkeyMobileLib
module. - Use the
TKMServiceFactoryBuilder
to create an instance of theTKMServiceFactory
in thewillFinishLaunchingWithOptions
callback in the app delegate.
import TapkeyMobileLib
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
private var TKMServiceFactory: TKMServiceFactory!
func application(_ application: UIApplication,
willFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Build service factory
self.TKMServiceFactory = TKMServiceFactoryBuilder()
.build()
}
}
Polling for Data¶
It is recommended to use Background App Refresh to poll the Tapkey Trust Service for notifications. This is configured in the app delegate:
// Background App Refresh
func application(
_ application: UIApplication,
performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Configure the Tapkey SDK to poll for notifications.
// Run the code via runAsyncInBackground to prevent the app from sleeping while fetching is in progress.
runAsyncInBackground(application, promise:
TKMServiceFactory.notificationManager
.pollForNotificationsAsync(cancellationToken: TKMCancellationTokens.None)
.finallyOnUi {
completionHandler(UIBackgroundFetchResult.newData)
}
)
}
NotificationManager#pollForNotificationsAsync()
can be used to manually check for updates. Manually checking for updated keys is recommended in the following situations: - After a user has been logged in to the SDK.
- After a new grant has been issued for a logged-in user.
- After a grant that affects the current user has been modified or revoked.
- After grants, affecting locks that one of the logged-in users has access to, have been revoked. The logged-in users might receive updated revocation information.
Logging In Users¶
The SDK takes a Tapkey access token to log in a user. Retrieving such an access token is part of the implementing application. Usually, either the Token Exchange or Authentication Code with PKCE grant type will be used. For more information about authenticating against Tapkey, take a look at the authentication section.
Once an access token has been obtained, it can be used to log in a user as outlined below:
TKMServiceFactory.userManager.logInAsync(accessToken: "eyJhbGciOiJSUz...O-YbBq8F7086rQi-kEbERp4dA3r0WonpHnmYcXEnA", cancellationToken: ct)
.continueOnUi(userId -> {
// User logged in, use user's ID here.
})
Subsequently, userManager.users
will yield a list of IDs of all logged-in users. In most applications, only one user will be logged in at a time.
val userId = userManager.users[0]
// Fetch information about the user using the ID
Note that a token refresh handler must be registered with the TKMServiceFactory
prior to logging in any users. This can be done using the TKMServiceFactoryBuilder
's setTokenRefreshHandler(tokenRefreshHandler: TKMTokenRefreshHandler)
method.
// In the `willFinishLaunchingWithOptions` callback in the app delegate:
// Build service factory
self.tapkeyServiceFactory = TKMServiceFactoryBuilder()
.setTokenRefreshHandler(SampleTokenRefreshHandler())
.build()
// Sample token refresh handler implementation
import RxSwift
import TapkeyMobileLib
class SampleTokenRefreshHandler: TKMTokenRefreshHandler {
func refreshAuthenticationAsync(userId: String, cancellationToken: TKMCancellationToken) -> TKMPromise<String> {
let promiseSource = TKMPromiseSource<String>()
let accessToken = getNewTapkeyAccessTokenFromCustomApplicationLogic()
// OK:
promiseSource.setResult(value)
// Or, in case no access token could be obtained:
promiseSource.setError(TKMError(errorDescriptor: TKMErrorDescriptor(
code: TKMAuthenticationHandlerErrorCodes.TokenRefreshFailed,
message: "No new token can be obtained.",
details: nil)))
return promiseSource.promise
}
func onRefreshFailed(userId: String) {
/*
* Will be called if re-authentication failed and a manual call to
* userManager.updateAccessToken() is required.
*/
}
}
Searching for Nearby Locks¶
Searching for nearby locks can be done via the TKMBleLockScanner
. An instance of TKMBleLockScanner
can be obtained from the TKMServiceFactory
.
TKMBleLockScanner bleLockScanner = tapkeyServiceFactory.bleLockScanner
// Returns an observer registration to stop scanning once finished.
bleScanObserverRegistration = bleLockScanner.startForegroundScan()
To observe locks entering and leaving the range of this device, the TKMBleLockScanner
's observable
can be used.
// Listen for Tapkey locks coming into or leaving range.
nearbyLocksObserverRegistration = bleLockScanner.observable
.addObserver(locks -> {
// Handle nearby locks here, e.g. present them to the user.
})
// Alternatively, the locks property can be used to get a list of nearby locks in this moment.
nearbyLocks = bleLockScanner.locks
Make sure to stop scanning if the app does not required information about nearby locks anymore:
// Stop scanning for nearby locks.
if self.nearbyLocksObserverRegistration != nil {
self.nearbyLocksObserverRegistration!.close()
self.nearbyLocksObserverRegistration = nil
}
Triggering a Lock¶
After a nearby lock was found with the TKMBleLockScanner
, it can be opened using the trigger lock command, which is available from the TKMCommandExecutionFacade
. Any command inside the command execution facade, requires a TLCP connection to a lock. The following example uses TKMBleLockCommunicator
to establish a TCLP connection via BLE.
// Use the BLE lock communicator to send a command to the lock
return bleLockCommunicator.executeCommandAsync(
bluetoothAddress: bluetoothAddress,
physicalLockId: physicalLockId,
commandFunc: { tlcpConnection in
// Pass the TLCP connection to the command execution facade
commandExecutionFacade!.triggerLockAsync(
tlcpConnection,
cancellationToken: TKMCancellationTokens.None
)
},
cancellationToken: TKMCancellationTokens.None)
// Process the command's result
.continueOnUi({ commandResult in
let code: TKMCommandResult.TKMCommandResultCode = commandResult?.code ??
TKMCommandResult.TKMCommandResultCode.technicalError
switch code {
case TKMCommandResult.TKMCommandResultCode.ok:
return true
default:
return false
}
})
.catchOnUi({ (_: Error) -> Bool in
NSLog("Trigger lock failed")
return false
})