light-mode-image
Learn
Management & OperationsMigration Guides

iOS Holder SDK v6.0.0 Migration Guide

A comprehensive guide to migrating to iOS Holder SDK v6.0.0, covering breaking changes, new features, and step-by-step migration instructions.

This is a draft migration guide, shared ahead of general availability to help you gauge migration effort and plan ahead of the release. We will update this page to a final copy when the SDK is generally available.

Overview

This guide provides a comprehensive overview of the changes introduced in the iOS Holder SDK v6.0.0, including breaking changes, new features, and migration steps.

This release focuses on strengthening trust at issuance, improving predictability in credential handling, and increasing consistency across platforms. It introduces Wallet Attestation support, enabling issuers to verify wallet integrity before issuing credentials, while making holder-side integrations more reliable and easier to reason about in production environments.

Key Features

  • Wallet Attestation support: The iOS Holder SDK now supports Wallet Attestation, allowing your holder application to prove to issuers that it is a trusted wallet before credentials are issued. To enable this, the SDK is now tethered to a MATTR VII tenant, which issues wallet attestation tokens when required by an issuer.
  • Stronger application identity during issuance: Credential issuance flows now consistently use the application's client_id, ensuring the holder is accurately represented when interacting with issuers. This improves compatibility with issuers applying stricter controls.
  • More predictable credential retrieval results: Credential retrieval responses are now more structured and deterministic, explicitly identifying success or failure with guaranteed fields per result type.
  • Cross-platform alignment: Naming and response structures have been aligned with the Android Holder SDK, minimizing divergence for teams maintaining cross-platform applications.
  • Improved biometric and storage lifecycle handling: Edge cases in biometric authentication, storage lifecycle, and app lifecycle events (such as prewarming or reinstall scenarios) have been addressed.
  • General stability and performance improvements: Multiple refinements reduce integration friction, increase consistency, and improve overall reliability.

Breaking Changes

This section outlines the breaking changes introduced in v6.0.0 that require updates to your existing implementation:

#ChangeImpact
1SDK initialization now requires platform/tenant configurationYou must create a holder application on your MATTR VII tenant and update all initialization call sites to provide tenant configuration.
2Pre-authorized code flow now uses the application's client_id instead of a default identifierEnsure your application has a valid configured client_id and issuers recognize it.
3Credential retrieval result shape updated to explicitly identify success or failure with guaranteed fieldsUpdate result parsing logic to use success/failure branching.
4doctype renamed to docType in credential retrievalRename all usages of doctype to docType.
5MobileCredentialAuthenticationOption renamed to DeviceAuthenticationOptionUpdate all imports and type references.
6matchedCredentials is now non-optional for online presentationRemove optional handling; treat as an array and handle empty-array cases.
7VerificationResult renamed to MobileCredentialVerificationResult and aligned with AndroidUpdate all type references and downstream mapping logic.
8Wallet Attestation introduces new error scenariosUpdate error handling, logging, analytics, and support diagnostics to account for new wallet attestation error cases.
9challenge in requestMobileCredentials is now optional to align with AndroidRemove assumptions that challenge must always be supplied.
10VerifierAuthenticationResult aligned with Android structureUpdate verifier authentication result handling and exhaustive switches.

Migration Steps

Create a holder application on your MATTR VII tenant

The iOS Holder SDK is now tethered to a MATTR VII tenant. The SDK will not function without a holder application configuration on your tenant. This is required regardless of whether wallet attestation is used in your credential flows.

To register your iOS application, make a request to create a holder application:

Request
POST /v1/holder/applications
Request body
{
    "name": "My iOS Holder Application",
    "clientId": "your-wallet-client-id",
    "type": "ios",
    "bundleId": "com.yourcompany.holderapp",
    "teamId": "YOUR_APPLE_TEAM_ID"
}
  • name: A unique name to identify your holder application.
  • clientId: The OAuth 2.0 client_id that identifies your wallet application. This value is included in attestation JWTs and must match the client_id configured on the issuer's Authorization Server.
  • type: Must be ios for an iOS application.
  • bundleId: The Bundle ID of your iOS app (must match your Xcode project configuration).
  • teamId: Your Apple Developer Team ID.

Once created, your holder application will be able to use the SDK and interact with the MATTR VII platform (for example, to obtain attestation tokens).

The response will include a unique id for your application, which must be used when initializing the SDK so that it can correctly identify and authenticate your application.

The clientId you configure here is the same value you must:

  1. Pass as the clientId parameter when calling retrieveCredentials.
  2. Register with each issuer you intend to interact with, so the issuer can identify and trust requests coming from your wallet application.

Update SDK initialization

SDK initialization now requires platform configuration. Update initialization calls to provide the required configuration:

- try await MobileCredentialHolder.shared.initialize(instanceID: instanceID)
+ let platformConfig = PlatformConfiguration(
+     tenantUrl: "https://your-tenant.vii.mattr.global",
+     appIdentifier: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ try await MobileCredentialHolder.shared.initialize(
+     instanceID: instanceID,
+     platformConfiguration: platformConfig
+ )
  • tenantUrl: The URL of your MATTR VII tenant. This must be the tenant where your holder application is configured.
  • appIdentifier: The id of your configured MATTR VII holder application.

Handle new Wallet Attestation error modes

Wallet Attestation introduces new error scenarios that can occur during credential issuance. These errors may arise when:

  • The SDK requests an attestation token from your MATTR VII tenant.
  • The SDK presents wallet attestation credentials to an issuer's Authorization Server at its /token endpoint.
  • The SDK requests credentials from an issuer's /credential endpoint with DPoP-bound access tokens.

The following methods are affected:

  • retrieveCredentials — may throw new error cases and return new per-credential failure types.

New error cases will be added to MobileCredentialHolderError (a breaking change requiring updates to exhaustive switch statements), CredentialIssuanceError, and RetrieveCredentialError.

Update your error handling, logging, analytics, and support diagnostics to account for new wallet attestation error cases. An exhaustive list of new errors will be provided with the final migration guide.

Update client_id configuration

Previously, the client_id passed to retrieveCredentials was not shared with the issuer during the pre-authorized code flow, so any value would work. This is no longer the case — the SDK now presents the client_id to the issuer as part of wallet attestation, and the issuer validates it against its list of trusted wallet providers.

To prepare for this change:

  1. Coordinate with the issuer to register your wallet application as a trusted wallet provider. The issuer will provide you with a client_id that identifies your application.
  2. Pass the issuer-provided client_id when calling retrieveCredentials.
  let results = try await holder.retrieveCredentials(
      credentialOffer: offer,
-     clientId: "any-value",
+     clientId: "issuer-provided-client-id"
  )

Issuance flows that previously worked with an arbitrary client_id will fail if the issuer requires a trusted wallet provider. Ensure you have coordinated with each issuer and obtained the correct client_id before upgrading. Test direct issuance flows to confirm credentials are issued successfully.

Update credential retrieval result handling

RetrieveCredentialResult has been converted from a struct with optional fields to an enum with explicit .success and .failure cases. Each case carries guaranteed associated values, removing ambiguity from result handling.

The retrieveCredentials function returns [RetrieveCredentialResult] — an array with one result per offered credential. Update your iteration logic to use pattern matching:

  let results = try await holder.retrieveCredentials(options)
  for result in results {
-     if let credentialId = result.credentialId {
-         // Use result.docType and credentialId
-     } else if let error = result.error {
-         // Use result.docType and error
-     }
+     switch result {
+     case .success(let docType, let credentialId):
+         // docType and credentialId are guaranteed
+     case .failure(let docType, let error):
+         // docType and error are guaranteed
+     }
  }

Update tests and any downstream logic that checked for nil fields.

Rename doctype to docType

The credential retrieval field has been renamed from doctype to docType. Update all usages across your codebase:

- let documentType = credential.doctype
+ let documentType = credential.docType

Update models, serializers, mocks, tests, and any cross-platform abstraction layers.

Rename MobileCredentialAuthenticationOption to DeviceAuthenticationOption

Update all imports, type references, and configuration objects:

- let authOption: MobileCredentialAuthenticationOption = .biometryCurrentSet
+ let authOption: DeviceAuthenticationOption = .biometryCurrentSet

Update matchedCredentials handling for online presentation

The matchedCredentials property is now non-optional for online presentation. Remove optional handling and treat it as an array:

- if let matched = session.matchedCredentials {
-     // Handle matched credentials
- } else {
-     // Handle nil case
- }
+ // matchedCredentials is now always an array (may be empty)
+ if session.matchedCredentials.isEmpty {
+     // Handle no matched credentials
+ } else {
+     // Handle matched credentials
+ }

Update VerificationResult to MobileCredentialVerificationResult

The VerificationResult type has been renamed to MobileCredentialVerificationResult and aligned structurally with Android:

- let result: VerificationResult = ...
+ let result: MobileCredentialVerificationResult = ...

You will need to validate cross-platform result handling and update any downstream mapping logic.

Handle optional challenge in requestMobileCredentials

The challenge parameter is now optional in requestMobileCredentials. Review call sites and validation logic, and remove any app-side assumptions that challenge must always be supplied:

- // challenge was previously required
- let request = try holder.requestMobileCredentials(challenge: challenge, ...)
+ // challenge is now optional
+ let request = try holder.requestMobileCredentials(challenge: challenge, ...) // still works
+ let request = try holder.requestMobileCredentials(...) // also valid without challenge

Update Verifier Authentication Result handling

VerifierAuthenticationResult is no longer optional. Previously, if the verifier didn't provide authentication proof, the result was nil. Now, the SDK always returns a concrete value — if no authentication proof is provided, the result is .unsigned(origin: nil) rather than absent. This means you can always inspect the verifier authentication result without optional unwrapping.

For online presentation, where the value was previously always nil, it is now always .unsigned(origin:).

Update your code to remove optional handling and account for the new default:

- public internal(set) var verifierAuthenticationResult: VerifierAuthenticationResult?
+ public internal(set) var verifierAuthenticationResult: VerifierAuthenticationResult
  init(docType: MobileCredentialDataTypes.DocType,
       namespaces: [MobileCredentialDataTypes.NameSpace: [MobileCredentialDataTypes.ElementID: MobileCredentialDataTypes.IntentToRetain]],
-      verifierAuthenticationResult: VerifierAuthenticationResult? = nil) {
+      verifierAuthenticationResult: VerifierAuthenticationResult = .unsigned(origin: nil)) {

How would you rate this page?

Last updated on

On this page