iOS Holder SDK v5.0.0 Migration Guide
Overview
This guide provides a comprehensive overview of the changes introduced in MobileCredentialHolderSDK v5.0.0 for iOS, including breaking changes, new features, and migration steps.
Key Features
- Seamless, OS-native credential presentation (DC API support): The iOS Holder SDK now integrates with the Digital Credentials API, allowing credentials to be presented via an OS overlay without launching your holder app. The OS automatically surfaces only matching credentials across installed holders — reducing friction, avoiding dead ends, and significantly improving the user experience.
- Clearer OID4VCI interoperability (v1.0 alignment): The iOS Holder SDK now aligns with the finalized OID4VCI v1.0 specification (upgraded from draft-12). This alignment improves interoperability across the ecosystem, enabling other systems to clearly identify your supported version and feature set, ensuring smoother integrations and consistent behavior across platforms.
- Improved reliability in contactless flows: Enhanced BLE performance delivers more consistent proximity credential exchanges and faster engagements.
- Stronger cryptography and standards alignment: Updated COSE algorithms (as per RFC 9864) strengthen cryptographic compatibility and ensure continued compliance with evolving standards.
- General stability and performance improvements: Multiple refinements reduce integration friction, increase consistency across mobile environments, and improve overall user experience.
For a detailed list of changes included in this release, refer to the SDK Changelog.
Breaking Changes
This section outlines the breaking changes introduced in v5.0.0 that require updates to your existing implementation:
| # | Element | Change | Impact |
|---|---|---|---|
| 1 | MobileCredentialHolder.getCredential(credentialId:skipStatusCheck:) | Parameter renamed to fetchUpdatedStatusList: with inverted semantics | All call sites using skipStatusCheck: must be updated. |
| 2 | MobileCredentialHolder.deinitialize() | No longer async | Remove await from all call sites. |
| 3 | MobileCredentialHolder.initialize(...) | New dcConfiguration: param + @available(iOSApplicationExtension, unavailable) | Extensions must use new initializeAppExtension. |
| 4 | OfferedCredential | New required property credentialConfigurationId: String | Manual decoders must handle new field. |
| 5 | getCurrentLogFilePath() | New appGroup: param + unavailable in extensions | Source compatible in apps; breaks extensions. |
| 6 | VerifierAuthenticationResult | New enum case .unsigned(origin: String?) | Exhaustive switch statements must add new case. |
| 7 | 20+ methods (see list below) | Now @available(iOSApplicationExtension, unavailable) | Extensions must migrate to DC APIs. |
| 8 | Xcode / Toolchain | SDK built with Xcode 26.0.0 | Requires Xcode 26.0.0+. Builds will fail on earlier toolchains (e.g. Xcode 16.4). CI environments must be upgraded. |
The following methods are unavailable in App Extensions:
- Initialization and lifecycle:
initializedeinitializedestroy
- Credential claiming:
discoverCredentialOffercreateAuthorizationSessionretrieveCredentials
- Credential management:
addCredentialdeleteCredentialgetCredentialsgetCredential
- Key and certificate management:
generateDeviceKeyaddTrustedIssuerCertificatesgetTrustedIssuerCertificatesdeleteTrustedIssuerCertificateaddTrustedVerifierCertificatesgetTrustedVerifierCertificatesdeleteTrustedVerifierCertificate
- Presentation sessions:
createProximityPresentationSessiongetCurrentProximityPresentationSessioncreateOnlinePresentationSessionProximityPresentationSession.sendResponseterminateSessionOnlinePresentationSession.sendResponse
- Logging:
getCurrentLogFilePath
New Additions
Types (iOS 26+)
| Type | Purpose |
|---|---|
DCConfiguration | Configure Digital Credentials support (appGroup, supportedDocTypes) |
DCConfiguration.SupportedDocType | Enum: .mDL, .eudi, .euav, .photoid, .jpMnc |
DCError | Errors for DC operations |
DCPresentationSession | Handle system-initiated credential requests |
Methods
| Method | Availability | Purpose |
|---|---|---|
initializeAppExtension(instanceID:appGroup:loggerConfiguration:) | iOS 26+ | Initialize SDK in Identity Document Provider extension |
createDcPresentationSession(from:) | iOS 26+ | Create session from system ISO18013MobileDocumentRequestContext |
performRegistrationUpdates() | iOS 26+ | Sync credentials with system Identity Document Store |
Enum Cases
| Type | New Case |
|---|---|
VerifierAuthenticationResult | .unsigned(origin: String?) |
MobileCredentialHolderError | .storageInitializedInBackground |
Protocol Conformances
| Type | Added Conformance |
|---|---|
VerifierAuthenticationError | Encodable |
Framework Imports
import IdentityDocumentServices // iOS 26+
import IdentityDocumentServicesUI // iOS 26+Backwards Compatible Changes
| Change | Note |
|---|---|
MobileCredentialDataTypes nested typealiases | @available removed (inherited from parent) |
OnlinePresentationSession.VerifiedBy | @available removed (inherited from parent) |
Deprecations
No new deprecations. Existing deprecations unchanged:
@available(*, deprecated, message: "Use .userPresence instead")
case biometricOrPasscode
@available(*, deprecated, message: "Use .biometryCurrentSet instead")
case biometricOnlyBug Fixes
- Fixed intermittent "unable to initialize storage" errors during app launch. The issue was caused by using
UserDefaultsforkeyIdstorage, which does not guarantee persistence—particularly during iOS app prewarming when protected data may be unavailable. The SDK now stores thekeyIdin the Keychain and performs explicit availability checks before initialization, throwing a newstorageInitializedInBackgrounderror when the keychain is inaccessible. - Fixed a crash that could occur when
getCredentials()orupdateMobileCredentialStatus()were called concurrently from multiple threads. The crash was caused by unsafe concurrent access to internal state. The SDK now properly serializes access to shared resources during credential operations.
Minimum Requirements
- iOS 15+ for core SDK functionality
- iOS 26+ for Digital Credentials API (DC API) features
Dependencies
Third party dependencies
- CBORCoding (MIT license)
- swift-certificates 1.7.0 (Apache-2.0 License)
- swift-asn1 1.3.1 (Apache-2.0 License)
Apple frameworks
- Security
- CryptoKit
- LocalAuthentication
- AuthenticationServices
- CoreBluetooth
- Combine
- OSLog
- UIKit
- AppKit (on macOS)
- IdentityDocumentServices
- IdentityDocumentServicesUI
Toolchain dependencies
- Swift 5.10.
- iOS support: The SDK functionality is only available for devices from iOS 15 onwards.
- Xcode: The SDK requires Xcode 26.0.0 (17A324) or later. Our CI builds use the latest stable Xcode available in GitHub Actions (setup-xcode action with the
latest-stablelabel). Ensure that your development environment and CI pipelines meet or exceed this minimum to avoid build failures. - macOS 15 or later.
Migration Steps
Update getCredential calls
The skipStatusCheck parameter has been renamed to fetchUpdatedStatusList with inverted semantics. When fetchUpdatedStatusList is true (default), the SDK will fetch the latest status list to ensure up-to-date revocation information. When false, it will skip fetching the status list. Update all calls accordingly:
- let credential = try await holder.getCredential(credentialId: id, skipStatusCheck: true)
+ let credential = try await holder.getCredential(credentialId: id, fetchUpdatedStatusList: false)| Old Parameter | New Parameter | Mapping |
|---|---|---|
skipStatusCheck: false (default) | fetchUpdatedStatusList: true (default) | No change needed |
skipStatusCheck: true | fetchUpdatedStatusList: false | Invert the boolean |
Refer to Revocation Status check for more information.
Update deinitialize calls
The deinitialize() method is no longer async, so remove await from all call sites:
- await MobileCredentialHolder.shared.deinitialize()
+ MobileCredentialHolder.shared.deinitialize()Handle OfferedCredential changes
The OfferedCredential struct now includes a new required property credentialConfigurationId: String. If you are using the default decoding provided by the SDK, no changes are needed. However, if you have implemented a custom decoder for OfferedCredential, you must update it to handle the new field:
struct OfferedCredential: Decodable {
+ let credentialConfigurationId: String
let docType: String
let claims: [Claim]
let name: String?
}Handle VerifierAuthenticationResult changes
The VerifierAuthenticationResult enum now includes a new case .unsigned(origin: String?) to represent unsigned/unauthenticated requests. If you have any exhaustive switch statements on VerifierAuthenticationResult, you must add a new case to handle this scenario:
switch verifierAuthenticationResult {
case .authenticated(let verifierInfo):
// Handle authenticated verifier
case .failed(let error):
// Handle authentication failure
+ case .unsigned(let origin):
+ // Handle unsigned/unauthenticated request (new in v5.0.0)
+ // origin contains the requesting website origin if available
}Enable Presenting Credentials via the Digital Credentials API (iOS 26+, optional)
The iOS Holder SDK v5.0.0 introduces support for the Digital Credentials API, allowing credentials to be presented via an OS-native overlay without launching your app. To enable this feature, you need to initialize the SDK with a DCConfiguration that specifies your supported document types and app group. This is optional but recommended for a seamless user experience.
// In main app initialization
if #available(iOS 26.0, *) {
let dcConfig = DCConfiguration(
appGroup: "group.com.yourcompany.app",
supportedDocTypes: [.mDL, .eudi]
)
try MobileCredentialHolder.shared.initialize(
instanceID: instanceID,
dcConfiguration: dcConfig
)
} else {
try MobileCredentialHolder.shared.initialize(instanceID: instanceID)
}App Extension Migration (iOS 26+)
The methods marked as @available(iOSApplicationExtension, unavailable) are no longer accessible from app extensions. If you have an Identity Document Provider extension, you must migrate to using the new Digital Credentials APIs for handling credential requests. This involves initializing the SDK for extension use and creating presentation sessions from the system-provided context.
// In your IdentityDocumentProviderExtension
@available(iOS 26.0, *)
func handleRequest(_ context: ISO18013MobileDocumentRequestContext) async throws {
// Initialize for extension context
try MobileCredentialHolder.shared.initializeAppExtension(
appGroup: "group.com.yourcompany.app"
)
// Create presentation session
let session = try MobileCredentialHolder.shared.createDcPresentationSession(from: context)
// Handle request...
try await session.sendResponse(credentialIDs: selectedCredentialIDs)
}Update Toolchain
The SDK now requires Xcode 26.0.0 (17A324) or later. Ensure that your development environment and CI pipelines are updated to use Xcode 26.0.0+ to avoid build failures.
How would you rate this page?
Last updated on