This guide covers breaking and behavioural changes when upgrading from v2.x to v3.0.
Minimum deployment target remains iOS 12.0. Swift concurrency helpers require iOS 13.0+.
| Area | v2.x | v3.0 |
|---|---|---|
| SDK construction | ThreeDS2SDK(configParameters:…) convenience init | ThreeDS2SDK() then initialize(…) |
| Init completion | ((Bool) -> Void)? | (Result<Void, R3DS2Error>) -> Void |
Init locale parameter | Present (ignored by SDK) | Removed |
| UI customization at init | Single UiCustomization? | UiCustomizationMap? (DEFAULT / DARK / MONOCHROME) |
| Dark-mode colours | setDarkTextColor, setDarkBackgroundColor, etc. | Removed — use separate map entries |
| SDK-owned UI strings | Hardcoded English | Localized via SDK string catalog (English shipped) |
| Init failure when metadata unavailable | Could complete with success == false | Result.failure(.sdkRuntime(…)) with explicit message |
Non-breaking additions: initialize(…) async throws and createTransaction(…) async throws on iOS 13+.
let sdk = ThreeDS2SDK(configParameters: config, uiCustomization: customization) { result in
switch result {
case .success: …
case .failure: …
}
}
// or legacy Bool callback
try sdk.initialize(configParameters: config, locale: nil, uiCustomization: customization) { success in
if success { … }
}
Always construct first, then initialize:
let sdk = ThreeDS2SDK()
do {
try sdk.initialize(configParameters: config, uiCustomization: customizationMap) { result in
switch result {
case .success:
// SDK ready — safe to call getSDKVersion(), createTransaction(), etc.
break
case .failure(let error):
// Async failure — typically metadata/operability (see §1.5)
break
}
}
} catch {
// Synchronous failure — invalid config or SDKAlreadyInitialized
}
let sdk = ThreeDS2SDK()
do {
try await sdk.initialize(configParameters: config, uiCustomization: customizationMap)
} catch let error as R3DS2Error {
…
} catch {
…
}
If you only need one theme and do not use dark/monochrome maps, the protocol still provides a convenience overload:
try sdk.initialize(configParameters: config, uiCustomization: singleCustomization) { result in
…
}
This wraps the customization as [.DEFAULT: singleCustomization]. The async throws overload accepts UiCustomizationMap? only.
These no longer exist in v3.0:
init(configParameters:uiCustomization:completion:)initialize(…, completion: ((Bool) -> Void)?)initialize(…, locale:…) (all overloads)locale parameterThe locale argument has been removed from initialization. It was never used by the SDK.
Device-information .locale (C005) is derived from Locale.current; .systemLocale (I010) from NSLocale.system, with a fallback to en_US_POSIX when the root system locale exposes no language-region components. Neither uses the removed init locale parameter.
Action: Remove locale: from all initialize call sites.
Replace Bool callbacks with Result<Void, R3DS2Error>:
// v2.x
try sdk.initialize(configParameters: config, uiCustomization: map) { success in
guard success else { return }
…
}
// v3.0
try sdk.initialize(configParameters: config, uiCustomization: map) { result in
switch result {
case .success:
…
case .failure(let error):
…
}
}
| When | How it surfaces |
|---|---|
| Invalid config (e.g. missing PAK) | Thrown synchronously from initialize(…) on the caller’s thread |
| Second init while already initialized / in progress | Thrown synchronously — .sdkAlreadyInitialized |
| Metadata download or operability check fails | Delivered in completion / async throw — .sdkRuntime(message:) |
After synchronous validation passes, metadata download and operability checks run on a background queue (DispatchQueue.global(qos: .userInitiated)).
Result-based completion — the handler is not called on the main queue:
try sdk.initialize(configParameters: config, uiCustomization: map) { result in
DispatchQueue.main.async {
switch result {
case .success: … // update UI here
case .failure: …
}
}
}
async throws (iOS 13+) — the awaiting task resumes on the main actor after metadata work completes. Synchronous validation failures from initialize(…) still throw on the caller’s thread before background work starts.
try await sdk.initialize(configParameters: config, uiCustomization: map)
// safe to update UI here when called from a MainActor context
Both async methods are not cancellable; once started, they run to completion.
Each ThreeDS2SDK instance should be used serially — do not call initialize, createTransaction, or cleanup concurrently on the same instance from multiple threads or unstructured Tasks.
v3.0 reports metadata problems explicitly instead of silently completing with false:
| Failure | Typical R3DS2Error | Message |
|---|---|---|
| SDK version metadata unavailable | .sdkRuntime | unable to obtain SDK version metadata |
| Required scheme RIDs missing from cache | .sdkRuntime | unable to obtain scheme data for: <RID>, … |
Configure required RIDs via the RIDS config parameter (ConfigParamType.registeredApplicationProviderIdentifiers). When omitted, init requires only usable SDK version metadata.
UiCustomizationMapPass a map keyed by UI mode instead of a single customization object:
let defaultTheme = UiCustomization()
// configure defaultTheme …
let darkTheme = UiCustomization()
// configure darkTheme …
let map: UiCustomizationMap = [
.DEFAULT: defaultTheme,
.DARK: darkTheme,
.MONOCHROME: darkTheme // optional; falls back to DEFAULT
]
try sdk.initialize(configParameters: config, uiCustomization: map) { result in … }
The SDK resolves the active entry at challenge UI time based on the current trait collection (light / dark / grayscale accessibility), and refreshes when traits change.
v2.x allowed light and dark colours on the same customization object:
// v2.x — removed in v3.0
try toolbar.setTextColor(hexColorCode: "#000000")
try toolbar.setDarkTextColor(hexColorCode: "#FFFFFF")
try toolbar.setBackgroundColor(hexColorCode: "#FFFFFF")
try toolbar.setDarkBackgroundColor(hexColorCode: "#1E293B")
v3.0 uses one colour set per UiCustomization instance. Provide separate instances in the map:
// v3.0
try defaultToolbar.setTextColor(hexColorCode: "#1E1955")
try defaultToolbar.setBackgroundColor(hexColorCode: "#5C46F6")
try darkToolbar.setTextColor(hexColorCode: "#F1F5F9")
try darkToolbar.setBackgroundColor(hexColorCode: "#1E293B")
let map: UiCustomizationMap = [
.DEFAULT: defaultUi,
.DARK: darkUi
]
Removed methods (all customization types):
setDarkTextColor(hexColorCode:)setDarkBackgroundColor(hexColorCode:)setDarkHeadingTextColor(hexColorCode:)setDarkBorderColor(hexColorCode:)getDarkTextColor(), getDarkBackgroundColor(), etc.| v2.x | v3.0 |
|---|---|
getTextboxCustomization() | getTextBoxCustomization() |
SDK-owned challenge chrome (toolbar defaults, cancel confirmation, whitelist Yes/No, alert buttons, OOB error dialog) is now loaded from the SDK’s string catalog (Localizable.xcstrings, English included in the XCFramework).
| String source | What it controls |
|---|---|
| SDK string catalog | Default toolbar title/cancel, cancel dialog, whitelist buttons, generic alerts |
UiCustomization toolbar text | Overrides catalog defaults when set |
| ACS challenge content | Unchanged — provided by the ACS |
Action: If you previously relied on hardcoded English matching SDK defaults, behaviour is unchanged for English. For other languages, either:
ToolbarCustomization.setHeaderText / setButtonText, orSecurity warning IDs (SW01–SW05) are unchanged and remain developer-facing English strings.
The following remain compatible (signatures unchanged):
createTransaction(directoryServerID:messageVersion:) — sync throw and Result-based completion; see §4.1 for the new async overloadgetAuthenticationRequestParameters(), doChallenge(…), getProgressView(), close()ChallengeStatusReceiver callbacksConfigParameters / addParam(…) / ConfigParamTypeR3DS2Error cases: .invalidInput, .sdkAlreadyInitialized, .sdkNotInitialized, .sdkRuntimeThreeDS2SDK, ConfigParameters, UiCustomization, etc.try await sdk.initialize(configParameters: config, uiCustomization: map)
let transaction = try await sdk.createTransaction(
directoryServerID: "A000000004",
messageVersion: "2.2.0"
)
let result = try await transaction.doChallenge(
challengeParameters: challengeParams,
timeOut: 5,
challengeView: challengeView
)
initialize, createTransaction, and doChallenge async throws methods wrap the existing callback-based APIs and resume on the main actor. They do not support cancellation. Use the completion-based overloads when you need background-queue delivery or iOS 12 support.
doChallenge returns a R3DS2ChallengeResult for terminal challenge outcomes (completed, cancelled, timedOut, protocolError, runtimeError). Synchronous validation failures are still thrown as R3DS2Error. The callback-based ChallengeStatusReceiver API remains available and unchanged.
TESTING_BUILD)Lab / certification builds expose a synchronous init without network metadata:
try sdk.initialize(configParameters: config, uiCustomization: map)
Changes from v2.x lab builds:
locale parameter removeduiCustomization is UiCustomizationMap?, not UiCustomization?Production Release XCFramework builds do not include TESTING_BUILD APIs.
ThreeDS2SDK(configParameters:…) with ThreeDS2SDK() + initialize(…)Result<Void, R3DS2Error>locale: argumentsUiCustomization? to UiCustomizationMap with .DEFAULT (and .DARK / .MONOCHROME as needed)setDark* calls with separate customization instances per modegetTextboxCustomization() → getTextBoxCustomization() if used.sdkRuntime init failures for metadata/operabilityasync throws resumes on the main actor)oobUniversalAppLinkSupported = "true" at initChallengeParameters.setThreeDSRequestorAppURL(...) for OOB returnSdkEventCode and EventDiagnostics (aligned with Android)| Option | Configuration | When to use |
|---|---|---|
| A — Dynamic (default) | Embed & sign v3.0.0 as for v2.0.1. No MERGED_BINARY_TYPE. | Simplest v2 → v3 upgrade |
| B — Mergeable | Set MERGED_BINARY_TYPE on app target (Xcode 15+). | Apps using mergeable libraries |
~> 3.0.0, or manual embed)MERGED_BINARY_TYPE on host app targetgetWarnings() on a clean production Release build on a physical deviceReference integration: see SDKManager.swift in the demo workspace (ravelin-3ds-sdk-ios-workspace/DemoUIKitLocalRef) for v3 init patterns with both completion handlers and async throws.
func setupSDK() {
let config = ConfigParameters()
try? config.addParam(paramType: .publishableApiKey, paramValue: "<your-pak>")
let defaultUi = buildDefaultUiCustomization()
let darkUi = buildDarkUiCustomization()
let map: UiCustomizationMap = [.DEFAULT: defaultUi, .DARK: darkUi]
let sdk = ThreeDS2SDK()
if #available(iOS 13.0, *) {
Task { @MainActor in
do {
try await sdk.initialize(configParameters: config, uiCustomization: map)
onSDKReady(sdk)
} catch {
onSDKFailed(error)
}
}
} else {
do {
try sdk.initialize(configParameters: config, uiCustomization: map) { result in
DispatchQueue.main.async {
switch result {
case .success: onSDKReady(sdk)
case .failure(let error): onSDKFailed(error)
}
}
}
} catch {
onSDKFailed(error)
}
}
}
Was this page helpful?