In general, every synchronous function that has error states can throw the XignSdkError in Swift or
XignSdkException in Kotlin. This exception has a property, the errorCode. The XignSdkErrorCode identifies the
error. You can base your error handling on these codes. The error code defines all possible error cases inside the
XignSys SDK. Meaning, not every function has all error states. Please refer to the specific function documentation, to
see which error cases can occur. The unexpected error is a special case. It wraps all errors that currently can not be
categorized more specifically, therefore this error can always be thrown potentially and should be handled accordingly.
There are some specific subclasses of the XignSdkError (XignSdkException) which can be thrown in special cases. For
example, there is a XignSdkHttpError (XignSdkHttpException), XignSdkLockoutError (XignSdkLockoutException) and a
XignSecurityInsufficientError (XignSecurityInsufficientException). The XignSdkHttpError (XignSdkHttpException)
contains the HTTP error code and can be thrown in functions that establish a connection with the server
like startAuthentication or decryptPersonalizationInitializationData. The XignSdkLockoutError is thrown if a
request was made during an active lockout. The XignSecurityInsufficientError
can be thrown by functions of the authentication and personalization processes.
private func synchronousErrorHandlingExample(
personalizationInitializationData: PersonalizationInitializationData,
activationPin: String
) throws {
let personalizer: Personalizer = XignSdk.shared.personalizer
let response: PersonalizationResponse
do {
response = try personalizer.decryptPersonalizationInitializationDataSynchronous(
data: personalizationInitializationData,
pin: activationPin
)
// carry on with the result
} catch let xignHttpError as XignSdkHttpError {
// This is a specific subclass of the `XignSdkError` which returns the HTTP error code.
// It can be used for debug purposes, otherwise the super class `XignSdkError` can be used
// because the `XignSdkHttpError.errorCode` is always `XignSdkErrorCodes.connectionError`.
let httpErrorCode: Int = xignHttpError.httpErrorCode
throw YourError.yourErrorCase
} catch let lockoutError as XignSdkLockoutError {
// Happens if the user is still locked out, due to an previous attempt and
// the lockout period was not waited out.
let information: PendingLockoutInformation = lockoutError.information
// Wait out the period given by the `PendingLockoutInformation` object.
// After that, the whole process must be started from the beginning; scanning the QR code.
throw YourError.yourErrorCase
} catch let xignSecurityInsufficientError as XignSdkSecurityInsufficientError {
// Indicates that the security level for that process was not reached with the current setup.
// For more information check chapter Security Check of the XignSys SDK documentation.
throw YourError.yourErrorCase
} catch let xignError as XignSdkError {
// map the general error to a specific error targeting the invoked function
let specificErrorCode = DecryptPersonalizationInitializationDataErrorCodes.from(xignError.errorCode)
// Handle the error cases according to their documentation. A default case should be avoided so
// new cases added in future SDK updates are not accidentally handled incorrectly.
switch specificErrorCode {
case .connectionError:
// Represents an connection error, e.g. no internet connection, server not reachable etc.
// If a HTTP is raised, the `xignError` can be of the more specific type `XignSdkHttpError`.
throw YourError.yourErrorCase
case .lockoutError:
// This error indicates that a request was denied by the server due to a pending lockout.
// In this case the `xignError` is of the more specific type `XignSdkLockoutError`.
// If the `XignSdkLockoutError` is handled beforehand, this case should not occur.
throw YourError.yourErrorCase
case .protocolVersionClientIncompatible:
// Indicates that the client version is below the server version. A client update
// is necessary.
throw YourError.yourErrorCase
case .protocolVersionServerIncompatible:
// Indicates that the server version is below the clients version.
throw YourError.yourErrorCase
case .deviceUnknown:
// Indicates that server doesn't recognizes the device. This could happen if the
// personalization was overridden by a device.
throw YourError.yourErrorCase
case .pinMissing:
// Raised if a PIN is need but wasn't supplied.
throw YourError.yourErrorCase
case .unknownRegistration:
// Returns if the XignIn-Manager cannot find an existing registration.
// The registration may have been deleted.
throw YourError.yourErrorCase
case .denied:
// Returned, if a merge is triggered and the registration to be merged is already
// personalized by a different device or it is not created via an organization invite.
throw YourError.yourErrorCase
case .alreadyUsed:
// Indicates that the personalization process initialization data of this particular
// activation have already used for a personalization process and are not usable again.
// This error can be thrown if the configuration of the XignIn-Manager prohibits a
// re-personalization, e.g. due to legal requirements.
throw YourError.yourErrorCase
case .securityInsufficient:
// Indicates that the security level for that process was not reached with the current setup. This can for
// example indicate that the version of the app/operation system is to low.
// In this case the `xignError` is of the more specific type `XignSecurityInsufficientError`.
// If the `XignSecurityInsufficientError` is handled beforehand, this case should not occur.
throw YourError.yourErrorCase
case .unexpectedError:
// Reflects unexpected errors that normally should not happen. For example, a
// `String` can not be converted to utf8 array etc. Currently such errors are covered
// by this code.
throw YourError.yourErrorCase
case .keyRemovedBySystem:
// Indicates that a key within the Secure Enclave has been removed by the system.
//
// This could happen, if for example a key has been generated with
// [DeviceLockRequired.required](x-source-tag://DeviceLockRequired.required) and the device lock was removed
// afterwards.
throw YourError.yourErrorCase
}
} catch {
// Should not happen normally, because the SDK wraps all it's error's within a
// `XignSdkError`. A general error should be displayed here, just in case.
throw YourError.yourErrorCase
}
}
private fun synchronousErrorHandlingExample(
personalizationInitializationData: PersonalizationInitializationData,
activationPin: String
) {
val personalizer: Personalizer = XignSdk.shared.personalizer
val response: PersonalizationResponse = try {
personalizer.decryptPersonalizationInitializationDataSynchronous(
personalizationInitializationData,
activationPin
)
// carry on with the result
} catch (xignHttpException: XignSdkHttpException) {
// This is a specific subclass of the `XignSdkException` which returns the HTTP error code.
// It can be used for debug purposes, otherwise the super class `XignSdkException` can be used
// because the `XignSdkHttpException.errorCode` is always `XignSdkErrorCodes.CONNECTION_ERROR`.
val httpErrorCode: Int = xignHttpException.httpErrorCode
throw YourException()
} catch (lockoutException: XignSdkLockoutException) {
// Happens if the user is still locked out, due to an previous attempt and
// the lockout period was not waited out.
val information: PendingLockoutInformation = lockoutException.information
// Wait out the period given by the `PendingLockoutInformation` object.
// After that, the whole process must be started from the beginning; scanning the QR code.
throw YourException()
} catch (securityInsufficientException: XignSdkSecurityInsufficientException) {
// Indicates that the security level for that process was not reached with the current setup.
// For more information check chapter Security Check of the XignSys SDK documentation.
throw YourException()
} catch (xignException: XignSdkException) {
// map the general error to a specific error targeting the invoked function
val specificErrorCode = DecryptPersonalizationInitializationDataErrorCodes.from(xignException.errorCode)
// Handle the error cases according to their documentation. A default case should be avoided so
// new cases added in future SDK updates are not accidentally handled incorrectly.
when (specificErrorCode) {
CONNECTION_ERROR -> {
// Represents an connection error, e.g. no internet connection, server not reachable etc.
// If a HTTP is raised, the `xignException` can be of the more specific type `XignSdkHttpException`.
throw YourException()
}
LOCKOUT_ERROR -> {
// This error indicates that a request was denied by the server due to a pending lockout.
// In this case the `xignException` is of the more specific type `XignSdkLockoutException`.
// If the `XignSdkLockoutException` is handled beforehand, this case should not occur.
throw YourException()
}
PROTOCOL_VERSION_CLIENT_INCOMPATIBLE -> {
// Indicates that the client version is below the server version. A client update
// is necessary.
throw YourException()
}
PROTOCOL_VERSION_SERVER_INCOMPATIBLE -> {
// Indicates that the server version is below the clients version.
throw YourException()
}
DEVICE_UNKNOWN -> {
// Indicates that server doesn't recognizes the device. This could happen if the
// personalization was overridden by a device.
throw YourException()
}
PIN_MISSING -> {
// Raised if a PIN is need but wasn't supplied.
throw YourException()
}
UNKNOWN_REGISTRATION -> {
// Returns if the XignIn-Manager cannot find an existing registration.
// The registration may have been deleted.
throw YourException()
}
DENIED -> {
// Returned if a merge is triggered and the registration to be merge is already
// personalized by a different device. Or if the registration to be merged is not
// created via a organization invite.
throw YourException()
}
ALREADY_USED -> {
// Indicates that the personalization process initialization data of this particular
// activation have already used for a personalization process and are not usable again.
// This error can be thrown if the configuration of the XignIn-Manager prohibits a
// re-personalization, e.g. due to legal requirements.
throw YourException()
}
UNEXPECTED_ERROR -> {
// Reflects unexpected errors that normally should not happen. For example, a
// `String` can not be converted to utf8 array etc. Currently such errors are covered
// by this code.
throw YourException()
}
SECURITY_INSUFFICIENT -> {
// Indicates that the security level for that process was not reached with the current setup. This can
// for example indicate that the version of the app/operation system is to low.
// In this case the `xignError` is of the more specific type `XignSecurityInsufficientError`.
// If the `XignSecurityInsufficientError` is handled beforehand, this case should not occur.
throw YourException()
}
KEY_REMOVED_BY_SYSTEM -> {
// This error occurs when a biometric factor operation has been performed but the underlying key has
// been removed by the system because the biometric samples have been modified by the user.
throw YourException()
}
else -> {
// Should not happen normally, because the SDK wraps all it's error's within a
// `XignSdkException`. A general error should be displayed here, just in case.
throw YourException()
}
}
}
}
For purpose of debugging and better issue reporting, you can consult the XignSdkError in Swift or the
XignSdkException in Kotlin for its cause, if one exists. This also applies to the message / description of the error.
DO NOT use these values for your error handling, they can change at any given time without notice in future updates.
The error handling for the asynchronous flow function is slightly different. To allow better code completion the error
codes are mapped to a specific enum. This enum only contains the errors that can be returned by the flow function. These
error codes are only a subset of the exiting ones. If you look at the function
DecryptPersonalizationInitializationDataObserver.onDecryptPersonalizationInitializationData error events are delivered
onDecryptPersonalizationInitializationDataObserver.onDecryptPersonalizationInitializationDataError. The callback
function has a parameter errorCode of the type DecryptPersonalizationInitializationDataErrorCodes. This enum only
contains errors that may actually occur.
func onDecryptPersonalizationInitializationDataError(
requestCode: RequestCode,
idmIdentifier: String,
errorCode: DecryptPersonalizationInitializationDataErrorCodes,
error: XignSdkError
) -> Bool {
if let xignHttpError = error as? XignSdkHttpError {
// This is a specific subclass of the `XignSdkError` which returns the HTTP error code.
// It can be used for debug purposes, otherwise the super class `XignSdkError` can be used
// because the `XignSdkHttpError.errorCode` is always `XignSdkErrorCodes.connectionError`.
let httpErrorCode: Int = xignHttpError.httpErrorCode
YourImplementation.handleTheError()
} else if let lockoutError = error as? XignSdkLockoutError {
// Happens if the user is still locked out, due to an previous attempt and
// the lockout period was not waited out.
let information: PendingLockoutInformation = lockoutError.information
// Wait out the period given by the `PendingLockoutInformation` object.
// After that, the whole process must be started from the beginning; scanning the QR code.
YourImplementation.handleTheError()
} else if let xignSecurityInsufficientError = error as? XignSdkSecurityInsufficientError {
// Indicates that the security level for that process was not reached with the current setup.
// For more information check chapter Security Check of the XignSys SDK documentation.
YourImplementation.handleTheError()
} else {
// Handle the error cases according to their documentation. A default case should be avoided so
// new cases added in future SDK updates are not accidentally handled incorrectly.
switch errorCode {
case .connectionError:
// Represents an connection error, e.g. no internet connection, server not reachable etc.
// If a HTTP is raised, the `error` can be of the more specific type `XignSdkHttpError`.
YourImplementation.handleTheError()
case .lockoutError:
// This error indicates that a request was denied by the server due to a pending lockout.
// In this case the `error` is of the more specific type `XignSdkLockoutError`.
// If the `XignSdkLockoutError` is handled beforehand, this case should not occur.
YourImplementation.handleTheError()
case .protocolVersionClientIncompatible:
// Indicates that the client version is below the server version. A client update
// is necessary.
YourImplementation.handleTheError()
case .protocolVersionServerIncompatible:
// Indicates that the server version is below the clients version.
YourImplementation.handleTheError()
case .deviceUnknown:
// Indicates that server doesn't recognizes the device. This could happen if the
// personalization was overridden by a device.
YourImplementation.handleTheError()
case .pinMissing:
// Raised if a PIN is need but wasn't supplied.
YourImplementation.handleTheError()
case .unknownRegistration:
// Returns if the XignIn-Manager cannot find an existing registration.
// The registration may have been deleted.
YourImplementation.handleTheError()
case .denied:
// Returned, if a merge is triggered and the registration to be merged is already
// personalized by a different device or it is not created via an organization invite.
YourImplementation.handleTheError()
case .alreadyUsed:
// Indicates that the personalization process initialization data of this particular
// activation have already used for a personalization process and are not usable again.
// This error can be thrown if the configuration of the XignIn-Manager prohibits a
// re-personalization, e.g. due to legal requirements.
YourImplementation.handleTheError()
case .securityInsufficient:
// Indicates that the security level for that process was not reached with the current setup. This can for
// example indicate that the version of the app/operation system is to low.
// In this case the `xignError` is of the more specific type `XignSecurityInsufficientError`.
// If the `XignSecurityInsufficientError` is handled beforehand, this case should not occur.
YourImplementation.handleTheError()
case .unexpectedError:
// Reflects unexpected errors that normally should not happen. For example, a
// `String` can not be converted to utf8 array etc. Currently such errors are covered
// by this code.
YourImplementation.handleTheError()
case .keyRemovedBySystem:
// Indicates that a key within the Secure Enclave has been removed by the system.
//
// This could happen, if for example a key has been generated with
// [DeviceLockRequired.required](x-source-tag://DeviceLockRequired.required) and the device lock was removed
// afterwards.
YourImplementation.handleTheError()
}
}
// return `true` to mark this event as handled, so it can be removed from the queue.
return true
}
fun onDecryptPersonalizationInitializationDataError(
requestCode: RequestCode,
idmIdentifier: String,
errorCode: DecryptPersonalizationInitializationDataErrorCodes,
error: XignSdkException
): Boolean {
when (error) {
is XignSdkHttpException -> {
// This is a specific subclass of the `XignSdkException` which returns the HTTP error code.
// It can be used for debug purposes, otherwise the super class `XignSdkException` can be used
// because the `XignSdkHttpException.errorCode` is always `XignSdkExceptionCodes.CONNECTION_ERROR`.
val httpErrorCode: Int = error.httpErrorCode
YourImplementation.handleTheException()
}
is XignSdkLockoutException -> {
// Happens if the user is still locked out, due to an previous attempt and
// the lockout period was not waited out.
val information: PendingLockoutInformation = error.information
// Wait out the period given by the `PendingLockoutInformation` object.
// After that, the whole process must be started from the beginning; scanning the QR code.
YourImplementation.handleTheException()
}
is XignSdkSecurityInsufficientException -> {
// Indicates that the security level for that process was not reached with the current setup.
// For more information check chapter Security Check of the XignSys SDK documentation.
YourImplementation.handleTheException()
}
else -> {
// Handle the error cases according to their documentation. A default case should be avoided so
// new cases added in future SDK updates are not accidentally handled incorrectly.
when (errorCode) {
CONNECTION_ERROR -> {
// Represents an connection error, e.g. no internet connection, server not reachable etc.
// If a HTTP is raised, the `error` can be of the more specific type `XignSdkHttpException`.
YourImplementation.handleTheException()
}
PROTOCOL_VERSION_CLIENT_INCOMPATIBLE -> {
// Indicates that the client version is below the server version. A client update
// is necessary.
YourImplementation.handleTheException()
}
PROTOCOL_VERSION_SERVER_INCOMPATIBLE -> {
// Indicates that the server version is below the clients version.
YourImplementation.handleTheException()
}
LOCKOUT_ERROR -> {
// This error indicates that a request was denied by the server due to a pending lockout.
// In this case the `error` is of the more specific type `XignSdkLockoutException`.
// If the `XignSdkLockoutException` is handled beforehand, this case should not occur.
YourImplementation.handleTheException()
}
DEVICE_UNKNOWN -> {
// Indicates that server doesn't recognizes the device. This could happen if the
// personalization was overridden by a device.
YourImplementation.handleTheException()
}
PIN_MISSING -> {
// Raised if a PIN is need but wasn't supplied.
YourImplementation.handleTheException()
}
UNKNOWN_REGISTRATION -> {
// Returns if the XignIn-Manager cannot find an existing registration.
// The registration may have been deleted.
YourImplementation.handleTheException()
}
DENIED -> {
// Returned if a merge is triggered and the registration to be merge is already
// personalized by a different device. Or if the registration to be merged is not
// created via a organization invite.
YourImplementation.handleTheException()
}
ALREADY_USED -> {
// Indicates that the personalization process initialization data of this particular
// activation have already used for a personalization process and are not usable again.
// This error can be thrown if the configuration of the XignIn-Manager prohibits a
// re-personalization, e.g. due to legal requirements.
YourImplementation.handleTheException()
}
SECURITY_INSUFFICIENT -> {
// Indicates that the security level for that process was not reached with the current setup. This
// can for example indicate that the version of the app/operation system is to low.
// In this case the `xignError` is of the more specific type `XignSecurityInsufficientError`.
// If the `XignSecurityInsufficientError` is handled beforehand, this case should not occur.
YourImplementation.handleTheException()
}
KEY_REMOVED_BY_SYSTEM -> {
// This error occurs when a biometric factor operation has been performed but the underlying key has
// been removed by the system because the biometric samples have been modified by the user.
YourImplementation.handleTheException()
}
UNEXPECTED_ERROR -> {
// Reflects unexpected errors that normally should not happen. For example, a
// `String` can not be converted to utf8 array etc. Currently such errors are covered
// by this code.
YourImplementation.handleTheException()
}
}
}
}
// return `true` to mark this event as handled, so it can be removed from the queue.
return true
}
This scheme is used for every asynchronous error callback function. All error code cases should be handled, hence a
default case should be omitted. Your IDE should then notify you if new cases are added in future releases.
The XignSdkError in Swift or the XignSdkException in Kotlin is also provided to the callback, but can be ignored.
This is only for the sake of troubleshooting and issue reporting.