Skip to main content
All SDK operations throw GrantivaError, which conforms to LocalizedError.

Error types

ErrorWhenSuggested action
deviceNotSupportedPhysical device doesn’t support App Attest (old hardware or restricted region)Degrade gracefully; this is a permanent device limitation
simulatorAPIKeyRequiredvalidateAttestation() called in the iOS Simulator without an API key configuredInitialize with Grantiva(teamId:apiKey:) — see Simulator Setup
attestationNotAvailableApp Attest not supported in this regionDegrade gracefully
networkError(Error)Network communication failedRetry with backoff
validationFailedServer rejected the attestationDevice may be compromised — block or challenge
tokenExpiredJWT token has expiredCall refreshToken() or validateAttestation()
configurationErrorInvalid Bundle ID or Team IDCheck SDK initialization
keyGenerationFailedCan’t create attestation keyRetry; may indicate Keychain issue
challengeExpiredServer challenge timed outRetry — the SDK handles this automatically
invalidResponseUnexpected server response formatCheck SDK/server version compatibility
rateLimitedToo many requestsBack off and retry after delay
feedbackNotAvailableFeedback not enabled for this tenantCheck your plan includes feedback
reattestRequiredServer reports attestation key has drifted (rpIdHash or signature mismatch)The SDK self-heals: it clears the cached keyId and re-runs attestation automatically. Avoid wrapping SDK calls in retry loops that re-call validateAttestation() — the SDK already retries once internally
serverError(reason: String)Server-side validation failure with a developer-readable reasonLog error.reason for diagnostics; do not show the raw reason to end users
keyAlreadyAttestedApple’s DCAppAttestService rejected attestKey because the keyId was already attested in a prior sessionThe SDK self-heals by clearing the stored keyId and generating a fresh one. If the retry also fails, this error surfaces to the caller — typically after a re-install or Keychain wipe

Example

do {
    let result = try await grantiva.validateAttestation()
} catch let error as GrantivaError {
    switch error {
    case .simulatorAPIKeyRequired:
        // Initialize with Grantiva(teamId:apiKey:) for Simulator builds
        print("Configure an API key for Simulator builds — see Simulator Setup")
    case .deviceNotSupported:
        // Physical device doesn't support App Attest (old hardware / restricted region)
        print("App Attest not available on this device")
    case .networkError(let underlying):
        print("Network error: \(underlying.localizedDescription)")
    case .validationFailed:
        print("Device verification failed")
    case .tokenExpired:
        let refreshed = try await grantiva.refreshToken()
    case .serverError(let reason):
        // Log reason for diagnostics
        print("Server error: \(reason)")
    case .rateLimited:
        try await Task.sleep(for: .seconds(5))
    default:
        print(error.localizedDescription)
    }
}

Localized descriptions

Every GrantivaError case provides a human-readable errorDescription:
GrantivaError.simulatorAPIKeyRequired.errorDescription
// "An API key is required to use Grantiva in the iOS Simulator"

GrantivaError.deviceNotSupported.errorDescription
// "This device does not support App Attest functionality"

GrantivaError.rateLimited.errorDescription
// "You have exceeded the rate limit for this action"

GrantivaError.serverError(reason: "invalid_key").errorDescription
// "Server error: invalid_key"