Integrating the Tapkey Mobile SDK for iOS¶
Latest Version
The latest version of the Tapkey Mobile SDK for iOS is 2.44.0.0
Warning
This documentation refers to version 2.44.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 for iOS is distributed as a compiled static Swift XCFramework for iOS and watchOS. It is built using following SDKs and versions:
Tooling | Version | Notice |
---|---|---|
Xcode | 16.0 | The Xcode version the framework is built with. Module Forward Compatiblity is enabled, so the SDK should also be compatible with future Xcode versions. |
Platforms | iphoneOS , watchOS | The framework is distributed as an XCFramework with support for these platforms. |
iphoneOS SDK | iOS 18.0 | The iphoneOS SDK the framework is built against. |
Minimum iOS Version | iOS 12 | The minimum iOS version the Tapkey Mobile SDK for iOS. |
watchOS SDK | watchOS 10.0 | The watchOS SDK version the framework is built against. |
Minimum watchOS Version | watchOS 6.2 | The minimum watchOS version the Tapkey Mobile SDK currently supports. |
Swift Compiler | 6.0 | The version of the Swift compiler that was used for compiling the current version of the Tapkey Mobile SDK for iOS (as bundled with Xcode). As Swift 5 does not offer backwards compatibility, the target app must be compiled using the same or a newer version. Read more about Swift Stability |
Cocoapods | 1.12.1 | The Cocoapods version the Tapkey Mobile SDK for iOS is distributed and tested with. Older version might not support static Swift frameworks or the XCFramework format. |
OjbC | n/a | The Tapkey Mobile SDK for iOS does not expose any ObjC APIs. |
Swift Package Manager | n/a | The Tapkey Mobile SDK for iOS is currently not available via the Swift Package Manager, as some of its dependencies are not available via Swift Package Manager as of today. |
Minimum Version Policy
The Tapkey Mobile SDK for iOS officially supports and is tested against the three latest major versions of iOS. E.g. if the current version of iOS is 18.0, then the Tapkey Mobile SDK for iOS is tested against iOS Version 16.0 and newer. Usually older versions are supported on best effort as well, but this could be discontinued at any time, should technical restrictions make this necessary. watchOS is currently only officially supported on the current major version.
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.44.0.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()
}
}
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 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 with the SDK's TKMBleLockScanner
. For more details or custom implementation please read the BLE Scanning section of the Documentation.
Usage Description in Info.plist required
Depending on the iOS SDK version the app is built against, different entries in the Info.plist
are required to scan for and communicate with BLE devices.
If case the reqired entry is missing, the app will crash using the bluetooth apis.
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
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.
let ct = TKMCancellationTokens.fromTimeout(timeoutMs: 15000)
// Use the BLE lock communicator to send a command to the lock
return bleLockCommunicator.executeCommandAsync(
peripheralId: peripheralId,
physicalLockId: physicalLockId,
commandFunc: { tlcpConnection -> TKMPromise<TKMCommandResult> in
// Pass the TLCP connection to the command execution facade
return self.commandExecutionFacade!.triggerLockAsync(
tlcpConnection,
cancellationToken: ct)
},
cancellationToken: ct)
// 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({ (e: TKMAsyncError) -> Bool in
let syncError = e.syncSrcError
// A TKMRuntimeError.bleInconsistencyError indicates that the bluetooth chip of the lock
// did not response as expected and should not happen. But the iPhone is known to cache some
// bluetooth connection specific cofigurations which might cause issues when the bluetooth
// configuration of the lock was changed during an firmware upgrade.
// In such very rare and unexpected cases a restart of the iphone should resolve the issue.
if case TKMRuntimeError.bleInconsistencyError(_) = syncError {
NSLog("Trigger lock failed because of a bleInconsistencyError")
// Inform the user that an unexpected happend and a restart of the iphone might help.
return false
}
NSLog("Trigger lock failed")
return false
})
Logging¶
By default the Tapkey Mobile Lib writes its logs to the system logging system. To be able to collect logs methodical for debugging and investigations, a custom logger can be used instead.
Info
A custom logger replaces the Tapkey default logger, therefore logs won't be written to LogCat anymore in such cases.
Create a custom implementation of the TKMLogger
.
class CustomLogger: TKMLogger {
func println(priority: Int32, tag: String, msg: String, error: Error?) {
// Custom logging implementation
}
}
The priority is divided into following static priority levels:
TKMLogs.VERBOSE
TKMLogs.DEBUG
TKMLogs.INFO
TKMLogs.WARN
TKMLogs.ERROR
Add an instance of the custom implementation to TKMLogs
. This should be done preferably before creating the TKMServiceFactoryBuilder to avoid lost logs during the bootrap.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
TKMLogs.setLogger(CustomLogger())
self.tapkeyServiceFactory = TKMServiceFactoryBuilder()
...
.build()
...
}