Migrating from v3
Starting with version 4.0.0, DataDomeAlamofire is built on top of the new CoreDataDome SDK instead of the legacy DataDomeSDK. This is a breaking change for anyone upgrading from the 3.x line.
The integration has been simplified: you now create a single DataDome instance and pass a DataDomeInterceptor directly to Alamofire.
This guide lists exactly what you need to change.
Requirements
- iOS 15+ (previously iOS 11)
- Xcode 16+
- A DataDome client-side key — available in your DataDome dashboard.
At a glance
| Area | Before (3.8.x) | After (4.0.0) |
|---|---|---|
| Package manager | CocoaPods or SPM | SPM only |
| Minimum iOS | 11 | 15 |
| Interceptor | AlamofireInterceptor(captchaDelegate:) | DataDomeInterceptor(dataDome:) |
| Wiring | Interceptor(adapter:retrier:) | pass DataDomeInterceptor directly |
| SDK instance | none to create | create & inject a DataDome instance |
| Info.plist | DataDomeKey (string) | DataDome › ClientSideKey (dictionary) |
Step 1: Update the dependency
CocoaPods is no longer supported
The new version is distributed only through Swift Package Manager. If your project integrated DataDomeAlamofire via CocoaPods, remove it from your Podfile:
target 'YourApp' do
# ❌ Remove this line
pod 'DataDomeAlamofire'
endThen run pod install (or pod deintegrate if DataDomeAlamofire was your only pod).
Add the package via SPM
In Xcode: File › Add Package Dependencies… and enter:
https://github.com/DataDome/datadome-alamofire-package.git
Finally, raise your app target's Minimum Deployments to iOS 15.0 or later.
Step 2: Update your Info.plist
The single DataDomeKey string is replaced by a DataDome dictionary containing ClientSideKey (and, optionally, your protected Domain).
Before
<key>DataDomeKey</key>
<string>YOUR_DATADOME_CLIENT_SIDE_KEY</string>After
<key>DataDome</key>
<dict>
<key>ClientSideKey</key>
<string>YOUR_DATADOME_CLIENT_SIDE_KEY</string>
<!-- Optional: your protected domain -->
<!-- <key>Domain</key>
<string>https://your-protected-domain.com</string> -->
</dict>Step 3: Create and inject a DataDome instance
DataDome instanceCreate a DataDome instance from a DataDomeConfiguration.
import CoreDataDome
/// Option A — provide the key in code (recommended)
let configuration = DataDomeConfiguration(clientKey: "YOUR_DATADOME_CLIENT_SIDE_KEY")
/// Option B — read the key from Info.plist (recommended)
// let configuration = try DataDomeConfiguration.configurationFromBundle()
let dataDome = DataDome(configuration: configuration)Step 4: Replace the interceptor wiring
DataDomeInterceptor conforms to Alamofire's RequestInterceptor, so you attach it directly — no more composing sessionAdapter and sessionRetrier into an Interceptor.
Before
import Alamofire
import DataDomeAlamofire
final class NetworkManager {
private let alamofireSession = Alamofire.Session(configuration: .default)
private let ddInterceptor = AlamofireInterceptor(captchaDelegate: nil)
private let interceptor: Alamofire.Interceptor
private init() {
interceptor = Interceptor(adapter: ddInterceptor.sessionAdapter,
retrier: ddInterceptor.sessionRetrier)
}
func protectedData(from url: URL) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
alamofireSession
.request(url, interceptor: interceptor)
.validate()
.responseData { response in
switch response.result {
case let .success(data): continuation.resume(returning: data)
case let .failure(error): continuation.resume(throwing: error)
}
}
}
}
}After
import Alamofire
import CoreDataDome
import DataDomeAlamofire
final class NetworkManager {
private let alamofireSession = Alamofire.Session(configuration: .default)
private let dataDome: DataDome
private let interceptor: DataDomeInterceptor
private init() {
let configuration = try! DataDomeConfiguration.configurationFromBundle()
dataDome = DataDome(configuration: configuration)
interceptor = DataDomeInterceptor(dataDome: dataDome)
}
func protectedData(from url: URL) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
alamofireSession
.request(url, interceptor: interceptor) // pass it directly
.validate() // keep .validate()
.responseData { response in
switch response.result {
case let .success(data): continuation.resume(returning: data)
case let .failure(error): continuation.resume(throwing: error)
}
}
}
}
}Keep
.validate(). A DataDome challenge is returned as an HTTP403..validate()turns that into a retriable error, which is what letsDataDomeInterceptorrun the validation and retry the request after the challenge is resolved.
You can also attach the interceptor once at the session level instead of per request:
let alamofireSession = Session(interceptor: interceptor)API mapping
| Removed / changed (old) | Replacement (new) |
|---|---|
import DataDomeSDK | import CoreDataDome |
AlamofireInterceptor(captchaDelegate:) | DataDomeInterceptor(dataDome:) |
interceptor.sessionAdapter / interceptor.sessionRetrier | pass DataDomeInterceptor directly as the RequestInterceptor |
Interceptor(adapter:retrier:) | (no longer needed) |
AlamofireAdapter, DataDomeAdapter | (removed) |
CaptchaDelegate | (removed) |
DataDomeKey (Info.plist) | DataDome › ClientSideKey |
DataDome.getCookie() | dataDome.getCookie(forURL:) |
DataDome.setCookie(_:) | dataDome.setCookie(_:) |
Behavioral changes
-
Challenge & block pages are presented automatically.
CaptchaDelegateparameter no longer exists. -
You can clear the DataDome cookie with
dataDome.unsafeClearCachedData:await dataDome.unsafeClearCachedData()
Updated 4 days ago

