Upgrade — iOS UIKit v5#
The Curity HAAPI iOS UIKit framework shipped its v5.0 major release on 2025-11-03, followed by minor releases through 5.6.0 (2026-08-10). This page covers the migration from v4.x to v5.x and highlights the notable additions in each 5.x release.
For the authoritative per-release detail, see the changelog on curity.io/docs/haapi-ios-ui-kit/latest/CHANGELOG.html.
Breaking Changes in v5.0#
Strict Concurrency#
The framework is now compiled with Strict Concurrency Checking set to Complete, Default Actor Isolation set to NonIsolated, and Approachable Concurrency enabled. Host apps with their own strict-concurrency settings should expect new diagnostics where types cross actor boundaries.
@MainActor is now explicit on these protocols and their members:
InteractionErrorModelInteractionValueModelInteractionItemSelectModelInteractionItemCheckboxModel
If you implement these protocols in custom UI mappers, your conforming types must satisfy the @MainActor constraint at the call sites where the framework invokes them.
Async Flow Control#
HaapiFlowViewModel.start, submit, and followLink are now async functions. Callers must await them inside a Task or async context.
// Before (v4.x)
viewModel.start()
// After (v5.0+)
Task {
await viewModel.start()
}
OAuthErrorModel Alignment#
OAuthErrorModel is aligned with the lower-layer ErrorTokenResponse (RFC 6749):
error_descriptionis now optionalerror_uri(optional) has been added
Code that force-unwraps errorDescription must guard against nil.
Breaking Changes in v5.1#
The HandleableProblemType enum gained a new case: tooManyAttemptsProblem. This is a source-breaking change only if you switch on HandleableProblemType without a default branch.
switch problemType {
case .invalidInputProblem: …
case .tooManyAttemptsProblem: … // ← new in 5.1
default: …
}
Breaking Changes in v5.6#
KeychainStorage is no longer public. The class had no public initializer and was never documented, so the only code affected is code that names the type — in a type annotation or a cast, for example. Build the framework’s keychain-backed store with StorageBuilder.Keychain() instead, and pass the result to HaapiUIKitConfigurationBuilder.setStorage(_:) (UI Layer) or HaapiConfiguration(storage:) (SDK Layer):
// 5.6+
let storage = StorageBuilder.Keychain()
.accessGroup("group.example.shared") // optional
.build() // -> Storage
Notable Additions Across 5.x#
5.1 (2025-12-15)#
HaapiUIKitConfigurationBuilder.setUsePasskeysBrowserFallback— configures behavior when Passkeys are not supported on the device.- Improved handling for
Too Many AttemptsandIncorrect Credentialsproblems inFormViewController.
5.2 (2026-02-09)#
preSubmithook inBaseViewControllernow uses the correctopenvisibility modifier so subclasses can override it.- Canceling a closing flow keeps the flow active rather than terminating it.
5.3 (2026-03-23)#
- New
BankIdViewControllerandBankIdModelaligned with latest BankID and accessibility requirements. HaapiResponseTypeableprotocol —UIInteractionModel,UIProblemModel, andUIOperationModelnow expose the originating HAAPI representation type viarepresentationType.isRequiredonInteractionValueModel(and the built-in input / checkbox / select models).FormViewControllerrenders a required-field asterisk on text and select fields during signup, using the configurederrorColorfromInputTextFieldstyle.- Polling models honor the server-provided interval; cadence fallback is server interval → BankID 1 s → configured
autoPollingDuration. - Logging migration —
setLogType()introduced; staticisXxxEnabledflags are deprecated. See Upgrade — HaapiLogger setLogType Migration.
5.4 (2026-05-04)#
HaapiUIPreviewer(DEBUG-only) — Xcode preview surface for theming iteration, component galleries, and embedded diagnostics overlay. Marked experimental; minor releases may introduce breaking changes.userCodemessage style forMessageView— 2-column monospace grid for recovery codes with a “Copy codes” action. AddsMessageStyleAttribute.usercodeandMessageView.UserCodetheming key.
5.5 (2026-06-15)#
ProblemModel.iss— optional issuer identifier of the authorization server, forwarded fromAuthorizationProblemfor OAuth issuer verification (RFC 9207). ExistingProblemModelconformers default tonil, so custom mappers need no source change.- BankID same-device and cross-device flows are handled as separate server responses, with the legacy combined response still supported. Legacy BankID v5 protocol handling was removed.
- QR code decoding runs off the main thread, so it no longer blocks BankID polling UI refreshes.
- SDK Layer —
HaapiAccessorBuilder.buildForOAuth()now accepts any configurationbuildForHaapi()accepts; a configuration carrying only aclientAuthenticationMethodpreviously threwHaapiError.invalidConfiguration. A DPoP signing failure during a token request no longer masks real server errors such asinvalid_grant. - Driver Layer — a failure generating an auxiliary header (DPoP proof, risk-assessment client-info JWT) no longer aborts the request; the failure is logged and that header is omitted.
5.6 (2026-08-10)#
- The HaapiUI Previewer now reaches consumer apps. The previewer added in 5.4 was compiled out of every shipped
xcframework— its#if DEBUGcode never survives a Release archive — so the documented API did not exist for customers. From 5.6.0 it ships as Swift source next to the binary and your own project compiles it: Debug builds get the previewer, Release builds compile it to an empty module.- Action required: add the
IdsvrHaapiUIKitPreviewerSPM product or CocoaPods pod next to the SDK; manual integrators copy theHaapiUIPreviewer/folder from the distribution repository at the same release tag. See Preview Tools. - Projects with custom build-configuration names (for example
Staging) must map them in thePodfile—project 'YourApp.xcodeproj', 'Staging' => :debug— otherwise CocoaPods applies its Release settings there and the previewer is unavailable. - The previewer API remains experimental; minor releases may change it.
- Action required: add the
- Configurable credential storage —
HaapiUIKitConfigurationBuilder.setStorage(_:)chooses where the authenticated session is persisted: the app-private keychain by default, a shared keychain access group to reach an App Extension, or a customStorage. The SDK Layer accepts the same store throughHaapiConfiguration(storage:), and the Driver Layer builds it withStorageBuilder.Keychain(). See Configuration (UI Layer) and Use in an App Extension or Widget.KeychainStorageis no longer public as part of this change — see Breaking Changes in v5.6 above. StorageError.unavailable— a typedcauseon the thrownStorageError.readError/.writeError/.deleteErrorfor a locked keychain or an unusable access group, carrying non-sensitive diagnostic context only. See Error Handling (Driver Layer).- Revoking a token-bound (DPoP) token now works. Revocation attaches a DPoP proof and retries once on the server’s
use_dpop_noncechallenge instead of failing with HTTP 401, andrevokeAccessTokensendstoken_type_hint=access_token(previously alwaysrefresh_token). No API change — existing revocation calls simply succeed. See Token Binding and OAuthTokenManager. - Driver Layer — the recoverable
use_dpop_nonceretry is logged at INFO; ERROR is now reserved for the non-recoverable case where the retry also fails. Log-based alerting keyed on that error should be re-checked.
5.7 (unreleased)#
- SDK Layer —
OAuthTokenManager.dpop(forAccessToken:)returns theDpopan access token is bound to, so a DPoP-bound access token can be presented to a resource server: sign a proof with it and send that in theDPoPheader alongsideAuthorization: DPoP <accessToken>. UI Layer integrators reach the same lookup throughOAuthLifecycle.dpop(forAccessToken:haapiUIKitConfiguration:). The resource server’s nonce remains the application’s to manage. See OAuthTokenManager. - Driver Layer — a DPoP proof now carries the
athclaim whenever it is created for an access token, rather than only when a DPoP nonce is also held. RFC 9449 §4.2 requires the claim whenever a proof accompanies an access token, and the first call to a resource server happens before any nonce has been issued. There is no API change, but the proofs on the wire differ: clients configured withuse-legacy-dpop=truewill now seeathon API-call proofs where it was previously omitted. See DPoP and Nonces.
Related Driver / SDK Migrations#
iOS UIKit composes the iOS SDK and Driver layers. The following lower-layer changes are visible to host apps that touch those types directly:
- DPoP Nonce Auto-Management — framework now manages the DPoP nonce internally (Driver v5.4).
- HaapiLogger setLogType Migration — logging API migration (v5.3).
Backward Compatibility#
- The
dpopNonceparameter onHaapiTokenManagerasync methods remains honored for backward compatibility through v5.x; new code should omit it. - Deprecated symbols are kept as bridges within v5.x and are candidates for removal in a future major.