iOS + Alamofire / Moya

How to integrate the DataDome SDK with Alamofire or Moya

📘

Upgrading from 3.8.x or earlier?

This guide covers a fresh v4 integration. For a step-by-step upgrade from a v3.x version, see the migration guide.

📘

Still integrating an older version of this SDK?

The v3.x documentation has been moved to this page.

Requirements

  • iOS 15.0+
  • Xcode 16.0+ (the SDK is compiled in the Swift 6 language mode)
  • Swift Package Manager only — CocoaPods support was removed in 4.0.0
  • Alamofire 5.0.0+
  • A DataDome client-side key, available in the DataDome Dashboard

Add the package

With Xcode

  1. In Xcode, open File ▸ Add Package Dependencies…
  2. Enter the package URL.
    https://github.com/DataDome/datadome-alamofire-package.git
  3. Set the dependency rule to Up to Next Major Version starting at 4.0.0.
  4. Add the DataDomeAlamofire library product to your application target.

With a Package.swift manifest

dependencies: [
    .package(url: "https://github.com/DataDome/datadome-alamofire-package.git", from: "4.0.0")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            .product(name: "DataDomeAlamofire", package: "datadome-alamofire-package")
        ]
    )
]

Configure your DataDome client-side key

You can provide your client-side key either programmatically or through your app's Info.plist.

Create the DataDome instance once and reuse it for the lifetime of your app (for example, as a property of a shared networking object).
It is an actor and is safe to share across tasks.

Option A: Programmatic

import CoreDataDome

let configuration = DataDomeConfiguration(clientKey: "YOUR_CLIENT_SIDE_KEY")
let dataDome = DataDome(configuration: configuration)

You can optionally pass a custom domain:

let configuration = DataDomeConfiguration(
    clientKey: "YOUR_CLIENT_SIDE_KEY",
    clientDomain: URL(string: "https://your-domain.com")
)

Option B: Info.plist

Add a DataDome dictionary to your Info.plist:

<key>DataDome</key>
<dict>
    <key>ClientSideKey</key>
    <string>YOUR_CLIENT_SIDE_KEY</string>
    <!-- Optional -->
    <key>Domain</key>
    <string>https://your-domain.com</string>
</dict>

Then build the configuration from the bundle:

import CoreDataDome

let configuration = try DataDomeConfiguration.configurationFromBundle()
let dataDome = DataDome(configuration: configuration)

DataDomeConfiguration.configurationFromBundle() throws if the DataDome dictionary or its ClientSideKey entry is missing.
Handle the error, or use the programmatic initializer instead.

Integrate with Alamofire

Create a DataDomeInterceptor from your DataDome instance and attach it to the requests you want to protect. DataDomeInterceptor conforms to Alamofire's RequestInterceptor, so you can pass it to either an individual request or the whole Session.

Per request

session.request(url, interceptor: interceptor)
	.validate()
	.responseData {
 		[...]
	}

Always call .validate().
DataDome challenges are served as HTTP 403 responses. The interceptor only runs its validation logic when Alamofire reports the request as failed, so without .validate() a 403 would be handed back to your completion handler as a "success" and the challenge would never be presented or retried.

Per session

let session = Alamofire.Session(configuration: .default, interceptor: interceptor)

Customizing the session

You are free to use your own Session and URLSessionConfiguration. If you do, keep the default cookie handling so the DataDome cookie set after a resolved challenge is attached to the retried request:

  • Leave configuration.httpShouldSetCookies = true (the default).
  • Keep the shared HTTPCookieStorage (configuration.httpCookieStorage = .shared, the default).

Disabling cookie storage prevents the DataDome cookie from being sent on the retried request, so the request will be challenged again in a loop.

Troubleshooting

SymptomResolution
No such module 'CoreDataDome'Make sure the DataDomeAlamofire product is added to your app target so SPM resolves CoreDataDome transitively.
Crash on launch / configurationFromBundle() throwsThe DataDome dictionary or its ClientSideKey entry is missing from Info.plist. Add it, or switch to the programmatic DataDomeConfiguration(clientKey:) initializer.
Challenges never appear / requests are never retriedEnsure the interceptor is attached to the request or session and that you call .validate() (or otherwise treat non-2xx responses as failures) so the 403 challenge is seen by the retrier.
Request keeps getting challenged in a loopCookie storage is disabled on your URLSessionConfiguration. Keep the default httpShouldSetCookies = true and the shared HTTPCookieStorage.
pod install cannot find the podCocoaPods support was removed in 4.0.0. Integrate via Swift Package Manager.
Cannot find type 'AlamofireInterceptor' / 'CaptchaDelegate' / 'DataDomeAdapter'These were removed in 4.0.0. Use DataDomeInterceptor(dataDome:); challenge presentation is now automatic.

What’s Next

Complete your integration by following the Configuration section on the main page

Did this page help you?