iOS Verifier SDK v6.0.0 Migration Guide
A comprehensive guide to migrating to iOS Verifier SDK v6.0.0, covering breaking changes, new features, and step-by-step migration instructions.
Overview
This guide provides a comprehensive overview of the changes introduced in the iOS Verifier SDK v6.0.0, including breaking changes, new features, and migration steps.
This release focuses on strengthening trust between your verifier application and your MATTR VII tenant, improving consistency across platforms, and making verification results more predictable. The headline change is SDK Tethering, which becomes required in this release: every SDK and app instance is now registered with, and licensed by, your MATTR VII tenant at initialization.
Unlike the Holder SDK, where SDK Tethering is optional, SDK Tethering is required for the Verifier
SDK from v6.0.0. This builds on an existing requirement — remote mobile (app-to-app) verification
already required you to supply a platformConfiguration so the SDK could reach your MATTR VII tenant
to handle the backend verification. That platformConfiguration is now mandatory for all
initializations and additionally drives SDK Tethering.
Key Features
- SDK Tethering (required): The iOS Verifier SDK is now tethered to a MATTR VII tenant, tying
each SDK/app instance to your tenant. On first initialization the SDK registers the app instance
with the tenant specified in
PlatformConfigurationand obtains a license; on subsequent initializations the existing license is renewed automatically. This lets you view registered and active app instances directly from your tenant for operational insight, and establishes a remote management channel we expect to extend in future releases (for example, remote syncing of trusted issuer lists and eventing). The SDK uses App Attest during app registration when available on the device. Network access is required when registration or renewal is performed. - Asynchronous initialization:
initializeis now asynchronous, aligning the SDK with modern Swift concurrency and the registration/licensing work performed during tethering. - Cross-platform alignment: Verification result types and the revocation status list API have been renamed and restructured to align with the Android Verifier SDK, minimizing divergence for teams maintaining cross-platform applications.
- Simpler challenge handling for remote verification: The
challengeparameter for remote mobile (app-to-app) verification is now optional; when omitted, the SDK generates a cryptographically secure challenge for you. - Clearer connectivity error reporting: Remote mobile verification now surfaces a dedicated
connectivityErrorwhen the internet connection is lost mid-flow. - 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:
| # | Change | Impact |
|---|---|---|
| 1 | SDK Tethering is now required: platformConfiguration is mandatory on initialize, which now also registers the app instance and obtains a license | Always supply a platformConfiguration. Handle the new invalidLicense and failedToRegister errors, which can be thrown by initialize and by most SDK APIs. |
| 2 | initialize is now asynchronous | Add await to all call sites and call initialize from an asynchronous context. |
| 3 | MobileCredentialVerifierError.platformConfigurationInvalid removed | Remove handling for this case; fetchAppleWalletConfiguration(request:merchantId:) no longer throws it. |
| 4 | VerificationResult renamed to MobileCredentialVerificationResult, with .reason renamed to .failureType | Update all type references, rename .reason to .failureType, and remove use of VerificationFailedReason. |
| 5 | TrustedCertificateVerificationResult.reason renamed to .failureType | Rename .reason to .failureType and remove use of VerificationFailedReason. |
| 6 | MobileCredentialVerificationFailureType now serializes as a {type, message} object instead of a plain raw-value string | Update any storage or transport layer that persists or forwards these serialized values. |
| 7 | TrustedCertificateVerificationFailureType now serializes as a {type, message} object instead of a plain raw-value string | Update any storage or transport layer that persists or forwards these serialized values. |
| 8 | Revocation status list methods and types renamed from TrustedIssuer-prefixed terminology to Revocation terminology | Rename updateTrustedIssuerStatusLists → refreshRevocationStatusLists, getTrustedIssuerStatusListsCacheInfo → getRevocationStatusListsCacheInfo, and the corresponding return types. |
| 9 | RevocationStatusListsRefreshResult and OnlinePresentationSessionResult converted from structs with optional properties to @frozen enums with success and failure cases | Replace property-based branching with switch/case pattern matching. |
| 10 | applicationId parameter removed from fetchAppleWalletConfiguration and requestMobileCredentials | Remove the applicationId argument from these call sites; the SDK now uses the applicationId from PlatformConfiguration. |
Migration Steps
Create a verifier application on your MATTR VII tenant
SDK Tethering requires a verifier application configured on the MATTR VII tenant your SDK connects to. If you already use remote mobile (app-to-app) verification you will have created one; the same application is reused for tethering. If you have not, create one now.
To register your iOS application, make a request to create a verifier application:
POST /v2/presentations/applications{
"name": "My iOS Verifier Application",
"type": "ios",
"bundleId": "com.yourcompany.verifierapp",
"teamId": "YOUR_APPLE_TEAM_ID"
}name: A unique name to identify your verifier application.type: Must beiosfor an iOS application.bundleId: The Bundle ID of your iOS app (must match your Xcode project configuration).teamId: Your Apple Developer Team ID.
The response will include a unique id for your application. This is the applicationId you supply
in PlatformConfiguration at initialization, which the SDK now uses for all flows (including remote
mobile verification).
Supply platformConfiguration and make initialize asynchronous
initialize is now asynchronous and platformConfiguration is required. Previously,
platformConfiguration was optional and only used for remote mobile (app-to-app) verification flows;
it now also drives SDK Tethering, registering the app instance with your MATTR VII tenant and
obtaining a license on first initialization.
Add await, call initialize from an asynchronous context, and always pass a
platformConfiguration:
- let platformConfiguration = PlatformConfiguration(
- tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!
- )
- try MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
+ let platformConfiguration = PlatformConfiguration(
+ tenantHost: URL(string: "https://your-tenant.vii.mattr.global")!,
+ applicationId: "1ef1f867-20b4-48ea-aec1-bea7aff4964c"
+ )
+ try await MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)tenantHost: The URL of your MATTR VII tenant where your verifier application is configured.applicationId: Theidof your configured iOS Verifier Application.
Network access is required the first time the SDK initializes (for registration) and when the license is renewed on subsequent initializations.
Handle license and registration errors
Because tethering registers and licenses the SDK, initialize can now throw
MobileCredentialVerifierError.invalidLicense and MobileCredentialVerifierError.failedToRegister.
The majority of the SDK's other APIs can now also throw invalidLicense when a valid license is not
present. Update your error handling, logging, analytics, and support diagnostics to account for these
cases:
do {
try await MobileCredentialVerifier.shared.initialize(platformConfiguration: platformConfiguration)
} catch {
switch error {
+ case MobileCredentialVerifierError.failedToRegister:
+ // Registration with the MATTR VII tenant failed — check connectivity and configuration
+ case MobileCredentialVerifierError.invalidLicense:
+ // The SDK license is missing, invalid, or expired
// ... other cases
}
}The MobileCredentialVerifierError.platformConfigurationInvalid case has been removed and is no
longer thrown by fetchAppleWalletConfiguration(request:merchantId:). Remove any handling for it.
Update VerificationResult to MobileCredentialVerificationResult
The VerificationResult type has been renamed to MobileCredentialVerificationResult and aligned
structurally with Android. Its reason property has been renamed to failureType, typed directly as
MobileCredentialVerificationFailureType? rather than the now-removed VerificationFailedReason
wrapper. MobileCredential.verificationResult and MobileCredentialPresentation.verificationResult
now return MobileCredentialVerificationResult:
- let result: VerificationResult = credential.verificationResult
+ let result: MobileCredentialVerificationResult = credential.verificationResult
- let failure = result.reason
+ let failure = result.failureTypeReplace all references to VerificationResult with MobileCredentialVerificationResult, rename
.reason to .failureType, and remove any usage of VerificationFailedReason.
Rename TrustedCertificateVerificationResult.reason to failureType
The same .reason → .failureType rename applies to TrustedCertificateVerificationResult. Its
failureType is now typed directly as TrustedCertificateVerificationFailureType? instead of the
now-removed VerificationFailedReason wrapper:
- let failure = trustedCertificateResult.reason
+ let failure = trustedCertificateResult.failureTypeUpdate failure-type serialization handling
MobileCredentialVerificationFailureType and TrustedCertificateVerificationFailureType now encode
and decode as a {type, message} object instead of a plain raw-value string:
- "TrustedIssuerCertificateNotFound"
+ {"type": "TrustedIssuerCertificateNotFound", "message": "Trusted issuer certificate not found"}If you persist or forward the serialized value of either failure type, update your storage or transport layer to produce and consume the new format.
Update revocation status list method and type names
The revocation status list management API has been renamed from TrustedIssuer-prefixed terminology
to Revocation terminology to better reflect its purpose — managing the lists used to check the
revocation status of credentials. Update all call sites to use the new method names and return types:
- let result = try await verifier.updateTrustedIssuerStatusLists()
+ let result = try await verifier.refreshRevocationStatusLists()
- let cacheInfo = verifier.getTrustedIssuerStatusListsCacheInfo()
+ let cacheInfo = try verifier.getRevocationStatusListsCacheInfo()| Old | New |
|---|---|
updateTrustedIssuerStatusLists() | refreshRevocationStatusLists() |
getTrustedIssuerStatusListsCacheInfo() | getRevocationStatusListsCacheInfo() |
UpdateTrustedIssuerStatusListsResult | RevocationStatusListsRefreshResult |
TrustedIssuerStatusListsCacheInfo | RevocationStatusListsCacheInfo |
Update result-type handling for @frozen enums
RevocationStatusListsRefreshResult and OnlinePresentationSessionResult have been converted from
structs with optional properties to @frozen enums with success and failure cases. Replace
property-based branching with switch/case pattern matching.
RevocationStatusListsRefreshResult.success carries nextUpdate: Date?, and .failure carries
nextUpdate: Date? and failedLists: [String: [String]]:
- let result = try await verifier.refreshRevocationStatusLists()
- if result.success {
- // Handle success
- } else {
- // Handle failure
- }
+ switch try await verifier.refreshRevocationStatusLists() {
+ case .success(let nextUpdate):
+ // All status lists refreshed; schedule the next refresh before nextUpdate
+ case .failure(let nextUpdate, let failedLists):
+ // failedLists holds the URIs that failed to refresh, keyed by trusted issuer certificate ID
+ }OnlinePresentationSessionResult.success carries sessionId: String, challenge: String?, and
mobileCredentialResponse: MobileCredentialResponse?; .failure carries sessionId: String,
challenge: String?, and error: OnlinePresentationResultError:
- if let response = result.mobileCredentialResponse {
- // Use response
- } else {
- // Use result.error
- }
+ switch result {
+ case .success(_, _, let mobileCredentialResponse):
+ // mobileCredentialResponse?.credentials holds the presented credentials
+ case .failure(_, _, let error):
+ // error.type and error.message describe the failure
+ }Remove the applicationId argument from fetchAppleWalletConfiguration and requestMobileCredentials
The applicationId parameter has been removed from fetchAppleWalletConfiguration and
requestMobileCredentials. The SDK now uses the applicationId supplied in PlatformConfiguration
during initialization. Remove the argument from your call sites:
let result = try await verifier.requestMobileCredentials(
request: [mobileCredentialRequest],
challenge: challenge,
- applicationId: "your-application-id"
)Ensure you provide applicationId via PlatformConfiguration (see the earlier step) instead.
(Optional) Simplify challenge handling for remote mobile verification
The challenge parameter in requestMobileCredentials for remote mobile (app-to-app) verification
is now optional. When omitted or left blank, the SDK generates a cryptographically secure random
32-byte challenge automatically, so you no longer need to manage challenge generation yourself:
- let result = try await verifier.requestMobileCredentials(
- request: [mobileCredentialRequest],
- challenge: UUID().uuidString
- )
+ let result = try await verifier.requestMobileCredentials(
+ request: [mobileCredentialRequest]
+ )This is an optional improvement; supplying your own challenge continues to work.
(Optional) Handle connectivity errors in remote mobile verification
requestMobileCredentials for remote mobile (app-to-app) verification now throws
MobileCredentialVerifierError.connectivityError if the internet connection is lost during the flow.
Handle this case to provide clear feedback and retry guidance to your users:
do {
let result = try await verifier.requestMobileCredentials(request: requests)
} catch {
+ if case MobileCredentialVerifierError.connectivityError = error {
+ // Prompt the user to check their connection and retry
+ }
}How would you rate this page?
Last updated on