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

AreaBefore (3.8.x)After (4.0.0)
Package managerCocoaPods or SPMSPM only
Minimum iOS1115
InterceptorAlamofireInterceptor(captchaDelegate:)DataDomeInterceptor(dataDome:)
WiringInterceptor(adapter:retrier:)pass DataDomeInterceptor directly
SDK instancenone to createcreate & inject a DataDome instance
Info.plistDataDomeKey (string)DataDomeClientSideKey (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'
end

Then 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

Create 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 HTTP 403. .validate() turns that into a retriable error, which is what lets DataDomeInterceptor run 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 DataDomeSDKimport CoreDataDome
AlamofireInterceptor(captchaDelegate:)DataDomeInterceptor(dataDome:)
interceptor.sessionAdapter / interceptor.sessionRetrierpass DataDomeInterceptor directly as the RequestInterceptor
Interceptor(adapter:retrier:)(no longer needed)
AlamofireAdapter, DataDomeAdapter(removed)
CaptchaDelegate(removed)
DataDomeKey (Info.plist)DataDomeClientSideKey
DataDome.getCookie()dataDome.getCookie(forURL:)
DataDome.setCookie(_:)dataDome.setCookie(_:)

Behavioral changes

  • Challenge & block pages are presented automatically. CaptchaDelegate parameter no longer exists.

  • You can clear the DataDome cookie with dataDome.unsafeClearCachedData:

    await dataDome.unsafeClearCachedData()

Did this page help you?