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:

  • InteractionErrorModel
  • InteractionValueModel
  • InteractionItemSelectModel
  • InteractionItemCheckboxModel

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_description is now optional
  • error_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 Attempts and Incorrect Credentials problems in FormViewController.

5.2 (2026-02-09)#

  • preSubmit hook in BaseViewController now uses the correct open visibility modifier so subclasses can override it.
  • Canceling a closing flow keeps the flow active rather than terminating it.

5.3 (2026-03-23)#

  • New BankIdViewController and BankIdModel aligned with latest BankID and accessibility requirements.
  • HaapiResponseTypeable protocol — UIInteractionModel, UIProblemModel, and UIOperationModel now expose the originating HAAPI representation type via representationType.
  • isRequired on InteractionValueModel (and the built-in input / checkbox / select models).
  • FormViewController renders a required-field asterisk on text and select fields during signup, using the configured errorColor from InputTextField style.
  • Polling models honor the server-provided interval; cadence fallback is server interval → BankID 1 s → configured autoPollingDuration.
  • Logging migration — setLogType() introduced; static isXxxEnabled flags 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.
  • userCode message style for MessageView — 2-column monospace grid for recovery codes with a “Copy codes” action. Adds MessageStyleAttribute.usercode and MessageView.UserCode theming key.

5.5 (2026-06-15)#

  • ProblemModel.iss — optional issuer identifier of the authorization server, forwarded from AuthorizationProblem for OAuth issuer verification (RFC 9207). Existing ProblemModel conformers default to nil, 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 configuration buildForHaapi() accepts; a configuration carrying only a clientAuthenticationMethod previously threw HaapiError.invalidConfiguration. A DPoP signing failure during a token request no longer masks real server errors such as invalid_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 DEBUG code 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 IdsvrHaapiUIKitPreviewer SPM product or CocoaPods pod next to the SDK; manual integrators copy the HaapiUIPreviewer/ 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 the Podfile — 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.
  • 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 custom Storage. The SDK Layer accepts the same store through HaapiConfiguration(storage:), and the Driver Layer builds it with StorageBuilder.Keychain(). See Configuration (UI Layer) and Use in an App Extension or Widget. KeychainStorage is no longer public as part of this change — see Breaking Changes in v5.6 above.
  • StorageError.unavailable — a typed cause on the thrown StorageError.readError / .writeError / .deleteError for 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_nonce challenge instead of failing with HTTP 401, and revokeAccessToken sends token_type_hint=access_token (previously always refresh_token). No API change — existing revocation calls simply succeed. See Token Binding and OAuthTokenManager.
  • Driver Layer — the recoverable use_dpop_nonce retry 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 the Dpop an 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 the DPoP header alongside Authorization: DPoP <accessToken>. UI Layer integrators reach the same lookup through OAuthLifecycle.dpop(forAccessToken:haapiUIKitConfiguration:). The resource server’s nonce remains the application’s to manage. See OAuthTokenManager.
  • Driver Layer — a DPoP proof now carries the ath claim 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 with use-legacy-dpop=true will now see ath on API-call proofs where it was previously omitted. See DPoP and Nonces.

iOS UIKit composes the iOS SDK and Driver layers. The following lower-layer changes are visible to host apps that touch those types directly:

Backward Compatibility#

  • The dpopNonce parameter on HaapiTokenManager async 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.

Was this helpful?