IDK usage¶
Getting started¶
To access APIs for the Digidentity IDK, the package/module must first be imported:
// Android
import com.digidentity.idk.DigidentityIDK
// iOS
import DigidentityIDK
The IDK instance must be configured before its APIs can be called. The IDK is provided as a singleton instance, so there is no need to cache it: once configured, it lives until the app is closed.
// Android
DigidentityIDK.configure(context, "client secret")
or
// Android
DigidentityIDK.configure(context, "client secret", null);
Parameters
- context: an instance of the
android.content.Context
. - secret: the string representation of the client secret provided by Digidentity. This is unique to the application and can not be modified.
- idkLogger: An instance of the
com.digidentity.idk.logger.IDKLogger
. Can be null.
// iOS
try DigidentityIDK.sharedInstance.configure(with: "client secret", delegate: self)
Parameters
- secret: the string representation of the client secret provided by Digidentity. This is unique to the application and can not be modified.
- delegate: used when the IDK requests the application to provide data and perform tasks. Please see IDKDelegate documentation for more information
Should the configuration fail, an error/exception will be thrown. This indicates that the client secret value may be incomplete or modified.
Completion closures¶
Some APIs return a value and accept either a success or failure closure parameter. In these cases, the result value will be returned before the completion closures are called.
Important: Closures may capture all objects referenced in their scope, leading to cycle references.
IDK errors¶
The IDK uses the common enum DigidentityIDKError
as the error type for all of its APIs, however some of the APIs may return specific, context-dependent errors. These errors are documented next to their respective API. Below are some common errors which may be returned by any API:
- notConfigured: the IDK has not been configured. The IDK must be configured before calling any of its APIs
- internalError: an internal error has occurred. A message value is included alongside the error only to provide further information
- flowCancelled: the flow has been dismissed or cancelled by the user
- flowClosed: the user clicked the close button in the Scan QR flow
- invalidInput: this error is context-dependent. If an API returns this error, more information can be found in the API's documentation
- unknownLoginState: the backend returned a login state which the IDK could not handle. Please inform our IDK developers should this occur
- deviceNotSupported: the IDK detected that the operating system or device is not supported
Some of the errors are only possible if feature flags are set:
- userForgotPIN: the feature flag customForgotPinButton
is set and the user tapped the "Forgot your PIN?" button
- productRegistrationNotAllowed: the feature flag disableProductRegistration
is set and the user reached a point when a IDK product registration is required
- operationNotAllowed: the feature flag disableManualAccountAddition
is set and the user reached a point when a manual IDK account addition is required
When an error occurs during a flow, the IDK attempts to present the user with options to recover from the error where possible. For example, the IDK may propose to try an operation again, send the user to app settings, or advise to contact customer support. The error is reported to the caller only in the case that the error was not possible to resolve (causing the flow to fail). When an IDK flow fails, in some cases an error dialog may be displayed to inform the user of what happened. In such cases, the failure closure is called only on the dialog dismiss action.
IDKDelegate (iOS)¶
The delegate is an object which helps the IDK perform tasks on behalf of the app. The delegate must be passed to the IDK via an argument when the IDK is configured. The IDK instance keeps a weak reference to the delegate in order to avoid a cycle reference, therefore it is the responsibility of the app to own the delegate object.
The IDK depends on Firebase Messaging which in turn relies on the Firebase Messaging token (FCM token) to establish a push notification communication channel. When the token is needed, the IDK asks the delegate to provide the token from the app's side. This call can occur multiple times and at any arbitrary moment.
// iOS
func firebaseTokenProvider(for idk: IDKProtocol, completion: @escaping (FirebaseToken?, Error?) -> Void) {
// pass the completion closure to Firebase:
Messaging.messaging().token(completion: completion)
}
Parameters
- idk: the IDK instance itself (matching the shared singleton)
- completion: this can usually be passed directly to Firebase Messaging (matching its signature)
Localization¶
The IDK currently supports Dutch and English localizations. The UI language is detected from the current device settings automatically. Where the device is set to a language that is not supported, the IDK will fall back to English.
UI customization¶
The UI of the IDK can be customized to match the visual style of your app through the json theme file where all supported visual attributes are set.
Android¶
On Android this is done by applying the IDK theme Gradle plugin and configuring a json theme file as its source file. For instructions please refer to the "IDK theme Gradle plugin setup" section of the Android setup. You can find an example of a json theme file here
iOS¶
On iOS this is done by passing the Theme
object to the IDK. The theme object is a json theme file mentioned earlier. You can find an example here.
Note: Customization is not mandatory: by default the IDK uses its built in Digidentity visual style.
The IDK supports automatic light/dark interface style switching. The theme file consists of three main parts (validation occurs during Theme
object initialization):
- colors: allows customization for colors in both the light and dark interface styles
- dimensions: allows standard size customization for points used in the IDK view layout
- textStyles: allows customization for font properties in captions
Important: There are no size validations for these values, so please ensure layouts are correct on all supported device screen sizes after customization.
// iOS
let themeFileURL: URL = ...
try let customTheme = Theme(themeFileURL)
DigidentityIDK.sharedInstance.setTheme(customTheme)
Customize font¶
Using a custom font can be achieved in three steps:
1. Add the custom font to your Xcode project.
2. Register the custom font by adding the font file name (with extension) to the Info.plist under the key Fonts provided by application (UIAppFonts).
3. Finally, in the json theme file, change fontFamily and style for the textStyles for which you want to use this custom font. The fontFamily and style should match with how they're named in the Font Book app. For example including Times New Roman Regular should be done as: "fontFamily": "Times New Roman"
and "style": "Regular"
.
Handling notifications¶
Registering for notifications¶
The IDK depends on Firebase Messaging. When Firebase delivers a new FCM token to the app, please provide the token to the IDK:
// Android
val token: String = ...
DigidentityIDK.handleNewFirebaseToken(token = token)
or
// Android
final String token = ...
DigidentityIDK.handleNewFirebaseToken(token);
// iOS
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
if let token = fcmToken {
DigidentityIDK.sharedInstance.handleNewFirebaseToken(token)
}
}
Processing notifications¶
Android¶
The IDK must be allowed to handle push notifications received by the app. Where messages are intended for the IDK, the IDK will handle them appropriately, otherwise the failure
closure is called with notDigidentityNotification
error to give the app a chance to handle the notification itself.
// Android
val context: Context = ...
val remoteMessage: RemoteMessage = ...
DigidentityIDK.handleNotification(
context = context,
message = remoteMessage,
onSuccess = {
// optional: respond to successful completion by user
},
onFailure = { digidentityIdkError ->
// optional: handle error
}
)
or
// Android
final Context context = ...
final RemoteMessage remoteMessage = ...
DigidentityIDK.handleNotification(
context,
remoteMessage,
() -> {
// optional: respond to successful completion by user
},
(digidentityIdkError) -> {
// optional: handle error
}
);
iOS¶
The IDK must be allowed to handle push notifications received by the app. Where messages are intended for the IDK, the IDK will handle them appropriately, otherwise failure
is called with notDigidentityNotification
error to give the app a chance to handle the notification itself.
// iOS
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
if notification.request.trigger is UNPushNotificationTrigger {
// Received remote notification while app is foreground. This should not be presented.
let userInfo = notification.request.content.userInfo
DigidentityIDK.sharedInstance.onRemoteNotificationReceived(
payload: userInfo,
success: {
// Notification is handled and the subsequent flow has successfully finished.
},
failure: { error in
// Notification handling failed or the subsequent flow execution failed.
switch error {
case .notDigidentityNotification:
// Not a Digidentity notification
break
default:
break
}
},
presentation: { viewController in
// Present view controller
})
completionHandler([])
} else {
// Received a local notification, which should be presented
completionHandler([.sound, .list, .banner])
}
}
In some cases the IDK may need to present a notification to the user, in which case a local notification may be sent via UNUserNotificationCenter
. Pass the local notification to the IDK to be handled:
// iOS
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let notificationRequest = response.notification.request
let userInfo = notificationRequest.content.userInfo
if notificationRequest.trigger is UNPushNotificationTrigger {
// Tapped on remote notification
DigidentityIDK.sharedInstance.onRemoteNotificationReceived(
payload: userInfo,
success: {
// Notification is handled and the subsequent flow has successfully finished.
},
failure: { error in
// Notification handling failed or the subsequent flow execution failed.
},
presentation: { navigationController in
// Present navigation controller
})
} else {
// Tapped on local notification
DigidentityIDK.sharedInstance.onLocalNotificationReceived(
payload: userInfo,
success: {
// Notification is handled and the subsequent flow has successfully finished.
},
failure: { error in
// Notification handling failed or the subsequent flow execution failed.
switch error {
case .notDigidentityNotification:
// Not a Digidentity notification
break
default:
break
}
},
presentation: { viewController in
// Present view controller
})
}
completionHandler()
}
Both onRemoteNotificationReceived
and onLocalNotificationReceived
have the same signature and the following applies to both methods:
Parameters
- payload: notification payload to be handled
- success: success closure. If no screen presentation required, called after notification is processed, otherwise called after presented flow succeeds and is dismissed
- failure: failure closure. If no screen presentation required, called after notification failed to process, otherwise called after presented flow fails and is dismissed. Delivers an error value such as the following:
- notDigidentityNotification: messages are not intended for the IDK
- presentation: presentation closure with
UINavigationController
as parameter. Called if IDK requires a screen to be presented. Screen dismisses itself after initiated flow is over
IDK flows¶
IDK flows are APIs which provide content screens completing specific tasks for the user. On iOS a screen may be represented as UIViewController
or UINavigationController
. This allows the IDK to navigate the user through a sequence of screens. The IDK controls IDK screen content and provides screens to the app, while the IDK screen presentation is controlled by the app itself.
Handling URLs¶
The IDK can handle URL input using Firebase Dynamic Links for URL resolution. For this to function, the URL format must be supported by both the IDK and the app (registered Universal Links for iOS). Depending on the content of the URL, the IDK may perform one of the following actions:
- start passwordless login flow
- start evidence upload flow
- start smart card creation flow
Android¶
// Android
val context: Context = ...
val intent: Intent = ...
val handleDeepLinkIntent = DigidentityIDK.handleDeepLink(
context = context,
intent = intent,
onSuccess = { handleUriResult ->
...
},
onFailure = { digidentityIdkError ->
...
}
)
context.startActivity(handleDeepLinkIntent)
or
// Android
final Context context = ...;
final Intent intent = ...;
final Intent handleDeepLinkIntent = DigidentityIDK.handleDeepLink(
context,
intent,
(handleUriResult) -> {
...;
return null;
},
(digidentityIdkError) -> {
...;
return null;
}
);
context.startActivity(handleDeepLinkIntent);
Parameters
- intent: an intent, which contains a Firebase Dynamic Link.
- onSuccess: success callback obtains a result value indicating which flow was started.
- onFailure: failure callback obtains an error value such as the following:
- CameraPermissionDenied: user denied permission to access the device camera. In this case the flow could not continue without camera access
- UriNotRecognized: the IDK could not recognize the provided URI. In this case the unrecognized URI is provided in the associated value. The IDK is only able to handle supported URIs
- UriActionNotSupported: the URL has been recognized, but is not currently supported by the IDK build
- InternalFailure: the URL has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
Return value
This action returns an Activity Intent
before running the callbacks.
iOS¶
// iOS
let someURL = ...
let idkController = DigidentityIDK.sharedInstance.handleURL(
url: someURL,
calledFromExternalApp: true,
success: { [weak self] result in
self?.dismissIDK()
},
failure: { [weak self] error in
self?.dismissIDK()
self?.handleIDKError(error)
}
)
self.present(idkController, animated: true)
Parameters
- url: url to handle
- calledFromExternalApp:: flag that to inform that the provided url was a deeplink opened from external app (default: false)
- success: success closure delivers a result value indicating which flow was started.
- failure: failure closure delivers an error value such as the following:
- cameraPermissionDenied: user denied permission to access the device camera. In this case the flow could not continue without camera access
- noUniquelyMatchedFirebaseDynamicLinkError: firebase could not uniquely match the provided URL to the current device (please refer to the Firebase documentation for more details)
- urlNotRecognized: the IDK could not recognize the provided URL. In this case, the unrecognized URL is provided in the associated value. The IDK is only able to handle supported URLs
- urlActionNotSupported: the URL has been recognized, but is not currently supported by the IDK build
- invalidInput: the URL has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
Return value
This action returns UINavigationController
before running closures, presented as a full screen.
Scan QR code fullscreen¶
The IDK can handle input in QR code form if the QR code contains a supported IDK URL. The purpose of QR code scanning is to extract an encoded URL and handle it according to the Handle URL API. In some cases the IDK will show an alert if something goes wrong. Include the optional parameter "showDialogOnFail = false" to always have the error returned instead of showing the alert. If omitted, the dialog will be shown in these cases.
Android¶
// Android
val context: Context = ...
val title: Int? = ...
val showDialogOnFail: Boolean = ..., //optional, can be omitted. Default value when omitted is true
val scanQrCodeFlowIntent = DigidentityIDK.scanQRCodeFlow(
context = context,
title = title,
onSuccess = { handleUriResult ->
...
},
onFailure = { digidentityIdkError ->
...
}
)
context.startActivity(scanQrCodeFlowIntent)
or
// Android
final Context context = ...;
final int title = ...;
final Intent scanQrCodeFlowIntent = DigidentityIDK.scanQRCodeFlow(
context,
title,
(handleUriResult) -> {
...;
return null;
},
(digidentityIdkError) -> {
...;
return null;
}
);
context.startActivity(scanQrCodeFlowIntent);
Parameters
- title: an optional string resource ID, which is used as a QR scanner screen title.
- onSuccess: success callback obtains a result value indicating which flow was started.
- onFailure: failure callback obtains an error value such as the following:
- CameraPermissionDenied: user denied permission to access the device camera. In this case the flow could not continue without camera access
- UriNotRecognized: the IDK could not recognize the provided URI. In this case the unrecognized URI is provided in the associated value. The IDK is only able to handle supported URIs
- UriActionNotSupported: the URI has been recognized, but is not currently supported by the IDK build
- InternalFailure: the URI has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
Return value
This action returns an Activity Intent
before running the callbacks.
iOS¶
// iOS
let idkController = IDK.sharedInstance.qrScanFlow(
showDialogOnFail: ...,
success: { [weak self] result in
self?.dismissIDK()
},
failure: { [weak self] error in
self?.dismissIDK()
self?.handleIDKError(error)
}
)
self.present(idkController, animated: true)
Parameters
- showDialogOnFail: boolean indicating if a dialog will be shown when qr scan fails, default = true
- success: the success closure delivers a result value indicating which flow was started
- failure: the failure closure delivers an error value such as the following:
- cameraPermissionDenied: user denied permission to access the device camera after the IDK displayed dialogs with a request and explanation. In this case, the user must go to system settings and grant camera access permission
- noUniquelyMatchedFirebaseDynamicLinkError: firebase could not uniquely match the provided URL to the current device (please refer to the Firebase documentation for more details)
- urlNotRecognized: the IDK could not recognize the provided URL. In this case, the unrecognized URL is provided in the associated value. The IDK is only able to handle supported URLs
- urlActionNotSupported: the URL has been recognized, but is not currently supported by the IDK build
- invalidInput: the URL has been recognized, but the IDK could not process it. The IDK displays an error dialog in this case
Return value
This action returns UINavigationController
before running closures, presented as a full screen.
Below is an example of how this may appear in the app: