> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudx.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Overview of CloudX iOS SDK setup and core features

Requires iOS 13.0+, Xcode 16.0+, and Swift 6.0+.

## Installation

### CocoaPods

```ruby Podfile theme={null}
platform :ios, '13.0'

target 'YourApp' do
  use_frameworks!

  # Core SDK
  pod 'CloudXCore', '~> 3.5.0'

  # Adapters for ad networks (add as needed).
  # Each adapter versions independently as <network-sdk-version>.<adapter-revision>
  # and is built against that exact network SDK version.
  pod 'CloudXMetaAdapter', '~> 6.21.1.0'            # FBAudienceNetwork 6.21.1
  pod 'CloudXVungleAdapter', '~> 7.7.4.0'           # VungleAds 7.7.4
  pod 'CloudXInMobiAdapter', '~> 11.3.0.0'          # InMobiSDK 11.3.0
  pod 'CloudXMintegralAdapter', '~> 8.1.5.0'        # MintegralAdSDK 8.1.5
  pod 'CloudXUnityAdsAdapter', '~> 4.19.0.0'        # UnityAds 4.19.0
  pod 'CloudXMagniteAdapterV2', '~> 1.0.0.1'        # MagniteSDK 1.0.0
  pod 'CloudXMobileFuseAdapter', '~> 1.11.0.0'      # MobileFuseSDK 1.11.0
  pod 'CloudXMolocoAdapter', '~> 4.8.0.0'           # MolocoSDKiOS 4.8.0
  pod 'CloudXVerveAdapter', '~> 3.9.0.0'            # HyBid 3.9.0
  pod 'CloudXDigitalTurbineAdapter', '~> 8.4.8.0'   # Fyber Marketplace SDK 8.4.8
  pod 'CloudXGoogleWaterfallAdapter', '~> 13.6.0.0' # Google Mobile Ads SDK 13.6.0
  pod 'CloudXPangleAdapter', '~> 7.9.1.3.0'         # Ads-Global (Pangle / ByteDance) 7.9.1.3
end
```

```bash theme={null}
pod install --repo-update
```

## Initialization

<CodeGroup>
  ```objc Objective-C theme={null}
  #import <CloudXCore/CloudXCore.h>

  CLXInitializationConfiguration *config =
      [CLXInitializationConfiguration configurationWithAppKey:@"your-app-key-here"];

  [[CloudXCore shared] initializeWithConfiguration:config completion:^(CLXSdkConfiguration *sdkConfig, CLXError * _Nullable error) {
      if (sdkConfig) {
          NSLog(@"CloudX SDK initialized successfully");
      } else {
          NSLog(@"Failed to initialize CloudX SDK: %@", error.localizedDescription);
      }
  }];
  ```

  ```swift Swift theme={null}
  import CloudXCore

  let config = CLXInitializationConfiguration.configuration(appKey: "your-app-key-here", builderBlock: nil)

  CloudXCore.shared.initialize(with: config) { sdkConfig, error in
      if sdkConfig != nil {
          print("CloudX SDK initialized successfully")
      } else {
          print("Failed to initialize CloudX SDK: \(error?.localizedDescription ?? "Unknown error")")
      }
  }
  ```
</CodeGroup>

## Ad Formats

CloudX supports banner, MREC, interstitial, rewarded, native, and app open ad integrations. Use the format-specific guides for implementation details:

<CardGroup cols={2}>
  <Card title="Banner & MREC ads" href="/en/ios/ad-formats/banner-mrec">
    Create fixed-size display placements with optional refresh control.
  </Card>

  <Card title="Interstitial ads" href="/en/ios/ad-formats/interstitial">
    Load and show full-screen interstitial placements.
  </Card>

  <Card title="Native ads" href="/en/ios/ad-formats/native">
    Render native creatives in custom app layouts.
  </Card>

  <Card title="Rewarded ads" href="/en/ios/ad-formats/rewarded">
    Reward users after completed rewarded ad views.
  </Card>

  <Card title="App Open ads" href="/en/ios/ad-formats/app-open">
    Full-screen placements for app launch and foreground moments.
  </Card>
</CardGroup>

### Ad Information (CLXAd)

The `CLXAd` object is passed to delegate callbacks and contains information about the loaded/displayed ad:

| Property             | Type                                   | Description                                                                       |
| -------------------- | -------------------------------------- | --------------------------------------------------------------------------------- |
| `adFormat`           | `CLXAdFormat`                          | Ad format (banner, MREC, interstitial, rewarded, native)                          |
| `adUnitId`           | `NSString?`                            | The ad unit ID                                                                    |
| `adUnitName`         | `NSString?`                            | The ad unit name                                                                  |
| `networkName`        | `NSString?`                            | Name of the winning ad network                                                    |
| `networkPlacement`   | `NSString?`                            | Network-specific placement ID                                                     |
| `placement`          | `NSString?`                            | Custom placement set via `placement` property                                     |
| `revenue`            | `NSNumber?`                            | Impression-level revenue in USD                                                   |
| `revenuePrecision`   | `NSString?`                            | Revenue precision, when provided by the winning network                           |
| `creativeIdentifier` | `NSString?`                            | Creative identifier for creative-level issue reporting                            |
| `requestLatency`     | `NSTimeInterval`                       | Time in seconds from ad request to ad response                                    |
| `nativeAd`           | `CLXNativeAd?`                         | Native ad asset container for native ads; `nil` for non-native formats            |
| `adValues`           | `NSDictionary<NSString *, NSString *>` | SDK-defined metadata for the loaded ad; values may be absent by format or network |

<CodeGroup>
  ```objc Objective-C theme={null}
  - (void)didLoadAd:(CLXAd *)ad {
      NSLog(@"Ad format: %ld", (long)ad.adFormat);
      NSLog(@"Network: %@", ad.networkName);
      NSLog(@"Revenue: %@", ad.revenue);
  }
  ```

  ```swift Swift theme={null}
  func didLoad(_ ad: CLXAd) {
      print("Ad format: \(ad.adFormat)")
      print("Network: \(ad.networkName ?? "unknown")")
      print("Revenue: \(ad.revenue ?? 0)")
  }
  ```
</CodeGroup>

### Error Handling

All SDK errors are returned as `CLXError` objects in delegate callbacks:

| Property               | Type           | Description                |
| ---------------------- | -------------- | -------------------------- |
| `code`                 | `CLXErrorCode` | Error category             |
| `localizedDescription` | `NSString`     | Human-readable description |
| `underlyingError`      | `NSError?`     | Optional underlying error  |

#### Error Code Categories

| Range   | Category       | Common Codes                                                                                                                          |
| ------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| 0       | General        | `CLXErrorCodeInternalError`                                                                                                           |
| 100-199 | Network        | `CLXErrorCodeNetworkError`, `CLXErrorCodeNetworkTimeout`, `CLXErrorCodeServerError`, `CLXErrorCodeNoConnection`                       |
| 200-299 | Initialization | `CLXErrorCodeNotInitialized`, `CLXErrorCodeSDKDisabled`, `CLXErrorCodeNoAdaptersFound`, `CLXErrorCodeInvalidAppKey`                   |
| 300-399 | Ad Loading     | `CLXErrorCodeNoFill`, `CLXErrorCodeInvalidAdUnit`, `CLXErrorCodeAdsDisabled`                                                          |
| 400-499 | Display        | `CLXErrorCodeAdNotReady`, `CLXErrorCodeAdAlreadyShowing`                                                                              |
| 600-699 | Adapter        | `CLXErrorCodeAdapterNoFill`, `CLXErrorCodeAdapterTimeout`, `CLXErrorCodeAdapterLoadTimeout`, `CLXErrorCodeAdapterInitializationError` |

## Advanced Features

### Debug Logging

<CodeGroup>
  ```objc Objective-C theme={null}
  [CloudXCore setMinLogLevel:CLXLogLevelDebug];  // Enable debug logging
  [CloudXCore setMinLogLevel:CLXLogLevelNone];   // Disable all logging
  ```

  ```swift Swift theme={null}
  CloudXCore.setMinLogLevel(.debug)  // Enable debug logging
  CloudXCore.setMinLogLevel(.none)   // Disable all logging
  ```
</CodeGroup>

**Log Levels:** `verbose` \< `debug` \< `info` \< `warn` \< `error` \< `none`

### Impression-Level Revenue Tracking

Set a `revenueDelegate` on any ad format to receive impression-level revenue (ILR) callbacks. The `CLXAd` object contains the revenue value in USD and the winning network name.

<CodeGroup>
  ```objc Objective-C theme={null}
  self.bannerAd.revenueDelegate = self;

  - (void)didPayRevenueForAd:(CLXAd *)ad {
      NSLog(@"Revenue: %@ from %@", ad.revenue, ad.networkName);
  }
  ```

  ```swift Swift theme={null}
  bannerAd?.revenueDelegate = self

  func didPayRevenue(for ad: CLXAd) {
      print("Revenue: \(ad.revenue ?? 0) from \(ad.networkName ?? "unknown")")
  }
  ```
</CodeGroup>

Works with all ad formats (banner, MREC, interstitial, rewarded, native).

### Publisher-Reported Revenue Data

If your app receives impression-level revenue callbacks or bid metadata from AdMob, InMobi, TopOn, or another mediation platform outside the CloudX ad flow, forward those events to CloudX after initialization:

| Field                     | Required | Description                                                                                                                                                                                                       |
| ------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `platform`                | Yes      | `CLXRevenuePlatformAdMob`, `CLXRevenuePlatformInMobi`, `CLXRevenuePlatformTopOn`, or `CLXRevenuePlatformCustom(@"MyProvider")` in Objective-C; `.adMob`, `.inMobi`, `.topOn`, or `.custom("MyProvider")` in Swift |
| `revenue`                 | Yes      | Revenue for one impression in the supplied currency, not CPM/eCPM                                                                                                                                                 |
| `adFormat`                | Yes      | Ad format string such as `banner`, `mrec`, `interstitial`, `rewarded`, `native`, or `app_open`                                                                                                                    |
| `currencyCode`            | No       | ISO 4217 currency code, if known                                                                                                                                                                                  |
| `precision`               | No       | `exact`, `estimated`, `publisherDefined`, or `undefined`                                                                                                                                                          |
| `networkName`             | No       | Winning ad network name, if known                                                                                                                                                                                 |
| `adUnitId`                | No       | Mediation-platform ad unit ID                                                                                                                                                                                     |
| `thirdPartyAdPlacementId` | No       | Network-side ad unit or placement ID                                                                                                                                                                              |
| `creativeId`              | No       | Creative ID from the ad network                                                                                                                                                                                   |
| `networkPlacement`        | No       | Network placement identifier                                                                                                                                                                                      |
| `countryCode`             | No       | User country code, if known                                                                                                                                                                                       |
| `userSegment`             | No       | User segment, if known                                                                                                                                                                                            |

`reportRevenueData(_:)` returns `true` when the event is accepted into the CloudX revenue pipeline. It returns `false` if the SDK is not initialized, server-side revenue tracking is disabled, or the platform name is blank. Acceptance is not a delivery guarantee.

#### AdMob paid events

AdMob iOS `AdValue.value` is already in the supplied currency units, so pass it directly to CloudX. Do not divide it by `1_000_000.0`.

<CodeGroup>
  ```objc Objective-C theme={null}
  static CLXRevenuePrecision *CLXRevenuePrecisionFromGAD(GADAdValuePrecision precision) {
      switch (precision) {
          case GADAdValuePrecisionPrecise: return CLXRevenuePrecision.exact;
          case GADAdValuePrecisionEstimated: return CLXRevenuePrecision.estimated;
          case GADAdValuePrecisionPublisherProvided: return CLXRevenuePrecision.publisherDefined;
          case GADAdValuePrecisionUnknown: return CLXRevenuePrecision.undefined;
      }
      return CLXRevenuePrecision.undefined;
  }

  - (BOOL)reportAdMobPaidEventWithAdValue:(GADAdValue *)adValue
                                 adFormat:(NSString *)adFormat
                                  adUnitId:(NSString *)adUnitId
                              responseInfo:(GADResponseInfo *)responseInfo {
      GADAdNetworkResponseInfo *servedBy = responseInfo.loadedAdNetworkResponseInfo;
      CLXRevenueData *data =
          [CLXRevenueData revenueDataWithPlatform:CLXRevenuePlatformAdMob
                                          revenue:adValue.value.doubleValue
                                         adFormat:adFormat
                                     builderBlock:^(CLXRevenueDataBuilder *builder) {
              builder.currencyCode = adValue.currencyCode;
              builder.precision = CLXRevenuePrecisionFromGAD(adValue.precision);
              builder.networkName = servedBy.adSourceName;
              builder.adUnitId = adUnitId;
              builder.thirdPartyAdPlacementId = servedBy.adSourceInstanceName;
          }];

      return [[CloudXCore shared] reportRevenueData:data];
  }

  __weak GADBannerView *weakBannerView = bannerView;
  bannerView.paidEventHandler = ^(GADAdValue *adValue) {
      [self reportAdMobPaidEventWithAdValue:adValue
                                   adFormat:@"banner"
                                    adUnitId:adUnitId
                                responseInfo:weakBannerView.responseInfo];
  };
  ```

  ```swift Swift theme={null}
  private func toCloudXRevenuePrecision(_ precision: AdValuePrecision) -> CLXRevenuePrecision {
      switch precision {
      case .precise:
          return .exact
      case .estimated:
          return .estimated
      case .publisherProvided:
          return .publisherDefined
      case .unknown:
          return .undefined
      @unknown default:
          return .undefined
      }
  }

  private func reportAdMobPaidEvent(
      _ adValue: AdValue,
      adFormat: String,
      adUnitId: String,
      responseInfo: ResponseInfo?
  ) -> Bool {
      let servedBy = responseInfo?.loadedAdNetworkResponseInfo
      let data = CLXRevenueData.revenueData(
          platform: .adMob,
          revenue: adValue.value.doubleValue,
          adFormat: adFormat
      ) { builder in
          builder.currencyCode = adValue.currencyCode
          builder.precision = toCloudXRevenuePrecision(adValue.precision)
          builder.networkName = servedBy?.adSourceName
          builder.adUnitId = adUnitId
          builder.thirdPartyAdPlacementId = servedBy?.adSourceInstanceName
      }

      return CloudXCore.shared.reportRevenueData(data)
  }

  bannerView.paidEventHandler = { [weak bannerView] adValue in
      _ = reportAdMobPaidEvent(
          adValue,
          adFormat: "banner",
          adUnitId: adUnitId,
          responseInfo: bannerView?.responseInfo
      )
  }
  ```
</CodeGroup>

#### InMobi impression events

For InMobi, save the `IMAdMetaInfo` object from `banner(_:didReceiveWithMetaInfo:)`. When `bannerAdImpressed(_:)` fires, send the saved `metaInfo.getBid()` to CloudX, then clear the saved value.

<CodeGroup>
  ```objc Objective-C theme={null}
  @property (nonatomic, strong, nullable) IMAdMetaInfo *latestInMobiMetaInfo;

  - (void)banner:(IMBanner *)banner didReceiveWithMetaInfo:(IMAdMetaInfo *)info {
      self.latestInMobiMetaInfo = info;
  }

  - (void)bannerAdImpressed:(IMBanner *)banner {
      if (!self.latestInMobiMetaInfo) {
          return;
      }

      [self reportInMobiImpressionWithMetaInfo:self.latestInMobiMetaInfo
                                      adFormat:@"banner"
                                   placementId:inMobiPlacementId];
      self.latestInMobiMetaInfo = nil;
  }

  - (BOOL)reportInMobiImpressionWithMetaInfo:(IMAdMetaInfo *)metaInfo
                                    adFormat:(NSString *)adFormat
                                 placementId:(NSString *)placementId {
      CLXRevenueData *data =
          [CLXRevenueData revenueDataWithPlatform:CLXRevenuePlatformInMobi
                                          revenue:[metaInfo getBid]
                                         adFormat:adFormat
                                     builderBlock:^(CLXRevenueDataBuilder *builder) {
              builder.precision = CLXRevenuePrecision.estimated;
              builder.thirdPartyAdPlacementId = placementId;
              builder.creativeId = metaInfo.creativeID;
          }];

      return [[CloudXCore shared] reportRevenueData:data];
  }
  ```

  ```swift Swift theme={null}
  private var latestInMobiMetaInfo: IMAdMetaInfo?

  func banner(_ banner: IMBanner, didReceiveWithMetaInfo info: IMAdMetaInfo) {
      latestInMobiMetaInfo = info
  }

  func bannerAdImpressed(_ banner: IMBanner) {
      guard let metaInfo = latestInMobiMetaInfo else {
          return
      }

      _ = reportInMobiImpression(
          metaInfo,
          adFormat: "banner",
          placementId: inMobiPlacementId
      )
      latestInMobiMetaInfo = nil
  }

  private func reportInMobiImpression(
      _ metaInfo: IMAdMetaInfo,
      adFormat: String,
      placementId: String
  ) -> Bool {
      let data = CLXRevenueData.revenueData(
          platform: .inMobi,
          revenue: metaInfo.getBid(),
          adFormat: adFormat
      ) { builder in
          builder.precision = .estimated
          builder.thirdPartyAdPlacementId = placementId
          builder.creativeId = metaInfo.creativeID
      }

      return CloudXCore.shared.reportRevenueData(data)
  }
  ```
</CodeGroup>

For interstitial and rewarded InMobi ads, use the same pattern with `IMInterstitialDelegate`: save `IMAdMetaInfo` in `interstitial(_:didReceiveWithMetaInfo:)`, then report it in `interstitialAdImpressed(_:)`.

#### TopOn revenue events

TopOn iOS reports revenue in `didRevenueForPlacementID:extra:`. Use `publisher_revenue` from `extra` as one-impression revenue and `currency` as the currency code.

<CodeGroup>
  ```objc Objective-C theme={null}
  static NSString *CLXTopOnStringValue(NSDictionary *extra, NSString *key) {
      id value = extra[key];
      return [value isKindOfClass:NSString.class] ? value : nil;
  }

  static CLXRevenuePrecision *CLXRevenuePrecisionFromTopOn(NSString *precision) {
      if ([precision isEqualToString:@"exact"]) return CLXRevenuePrecision.exact;
      if ([precision isEqualToString:@"estimated"]) return CLXRevenuePrecision.estimated;
      if ([precision isEqualToString:@"publisher_defined"]) return CLXRevenuePrecision.publisherDefined;
      return CLXRevenuePrecision.undefined;
  }

  - (BOOL)reportTopOnRevenueForPlacementID:(NSString *)placementID
                                     extra:(NSDictionary *)extra
                                  adFormat:(NSString *)adFormat {
      NSNumber *revenue = extra[@"publisher_revenue"];
      if (![revenue isKindOfClass:NSNumber.class]) {
          return NO;
      }

      CLXRevenueData *data =
          [CLXRevenueData revenueDataWithPlatform:CLXRevenuePlatformTopOn
                                          revenue:revenue.doubleValue
                                         adFormat:adFormat
                                     builderBlock:^(CLXRevenueDataBuilder *builder) {
              builder.currencyCode = CLXTopOnStringValue(extra, @"currency");
              builder.precision = CLXRevenuePrecisionFromTopOn(CLXTopOnStringValue(extra, @"precision"));
              builder.networkName = CLXTopOnStringValue(extra, @"network_name");
              builder.adUnitId = placementID;
              builder.thirdPartyAdPlacementId = CLXTopOnStringValue(extra, @"network_placement_id");
              builder.networkPlacement = CLXTopOnStringValue(extra, @"adsource_id");
              builder.countryCode = CLXTopOnStringValue(extra, @"country");
          }];

      return [[CloudXCore shared] reportRevenueData:data];
  }

  - (void)didRevenueForPlacementID:(NSString *)placementID extra:(NSDictionary *)extra {
      [self reportTopOnRevenueForPlacementID:placementID
                                       extra:extra
                                    adFormat:@"banner"];
  }
  ```

  ```swift Swift theme={null}
  private func topOnRevenuePrecision(_ precision: String?) -> CLXRevenuePrecision {
      switch precision {
      case "exact":
          return .exact
      case "estimated":
          return .estimated
      case "publisher_defined":
          return .publisherDefined
      default:
          return .undefined
      }
  }

  private func reportTopOnRevenue(
      placementID: String,
      extra: [AnyHashable: Any],
      adFormat: String
  ) -> Bool {
      guard let revenue = (extra["publisher_revenue"] as? NSNumber)?.doubleValue else {
          return false
      }

      let data = CLXRevenueData.revenueData(
          platform: .topOn,
          revenue: revenue,
          adFormat: adFormat
      ) { builder in
          builder.currencyCode = extra["currency"] as? String
          builder.precision = topOnRevenuePrecision(extra["precision"] as? String)
          builder.networkName = extra["network_name"] as? String
          builder.adUnitId = placementID
          builder.thirdPartyAdPlacementId = extra["network_placement_id"] as? String
          builder.networkPlacement = extra["adsource_id"] as? String
          builder.countryCode = extra["country"] as? String
      }

      return CloudXCore.shared.reportRevenueData(data)
  }

  func didRevenue(forPlacementID placementID: String, extra: [AnyHashable: Any]) {
      _ = reportTopOnRevenue(
          placementID: placementID,
          extra: extra,
          adFormat: "banner"
      )
  }
  ```
</CodeGroup>

#### Custom platform events

Use `Custom` for providers that do not have a CloudX SDK constant like AdMob, InMobi, and TopOn do. The value is only the provider name, such as `CLXRevenuePlatformCustom(@"TradPlus")` in Objective-C or `CLXRevenuePlatform.custom("TradPlus")` in Swift. Keep the name stable so CloudX can group that provider's revenue consistently.

Do not include the amount or currency in the provider name. For a TradPlus impression worth USD 0.01, send platform `TradPlus`, revenue `0.01`, and currency code `USD`. If your source reports CPM/eCPM, divide by `1_000.0` first.

<CodeGroup>
  ```objc Objective-C theme={null}
  - (BOOL)reportCustomRevenueEventWithProviderName:(NSString *)providerName
                                           revenue:(double)revenue
                                          adFormat:(NSString *)adFormat
                                      currencyCode:(NSString *)currencyCode
                                          adUnitId:(NSString *)adUnitId
                                       placementId:(NSString *)placementId {
      CLXRevenueData *data =
          [CLXRevenueData revenueDataWithPlatform:CLXRevenuePlatformCustom(providerName)
                                          revenue:revenue
                                         adFormat:adFormat
                                     builderBlock:^(CLXRevenueDataBuilder *builder) {
              builder.currencyCode = currencyCode;
              builder.precision = CLXRevenuePrecision.publisherDefined;
              builder.adUnitId = adUnitId;
              builder.thirdPartyAdPlacementId = placementId;
          }];

      return [[CloudXCore shared] reportRevenueData:data];
  }
  ```

  ```swift Swift theme={null}
  private func reportCustomRevenueEvent(
      providerName: String,
      revenue: Double,
      adFormat: String,
      currencyCode: String,
      adUnitId: String,
      placementId: String
  ) -> Bool {
      let data = CLXRevenueData.revenueData(
          platform: .custom(providerName),
          revenue: revenue,
          adFormat: adFormat
      ) { builder in
          builder.currencyCode = currencyCode
          builder.precision = .publisherDefined
          builder.adUnitId = adUnitId
          builder.thirdPartyAdPlacementId = placementId
      }

      return CloudXCore.shared.reportRevenueData(data)
  }
  ```
</CodeGroup>

### Delegate Threading

Publisher delegate callbacks are delivered on the main queue and may fire inline relative to the SDK call that triggered them. Keep delegate handlers re-entrant-safe if they call back into the SDK.

### Test Mode

Test mode is **server-controlled** via device whitelisting. This provides better security and control over which devices receive test ads.

**To enable test mode:**

1. Initialize the SDK and check the logs for your device IFA:
   ```
   [CloudX][INFO] Device IFA for test whitelisting: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
   ```

2. Copy the IFA and add it to your device whitelist on the CloudX server dashboard

3. The SDK will automatically configure adapters for test mode and include the test flag in bid requests

<Note>
  Test mode is determined by the server, so you don't need to change any code between development and production builds.
</Note>

### Privacy Compliance

The CloudX SDK supports GDPR and CCPA privacy compliance by reading standard IAB privacy strings from `NSUserDefaults`. These values are typically set automatically by your Consent Management Platform (CMP) such as Google UMP, OneTrust, or Sourcepoint.

#### How It Works

The SDK automatically detects user location and reads consent signals:

1. **EU Users (GDPR)**: Checks TCF v2 consent for purposes 1 and 2 per the [IAB Global Vendor List](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework) and vendor consent ([CloudX Vendor ID: 1510](https://vendor-list.consensu.org/v3/vendor-list.json))
2. **US Users (CCPA)**: Checks for sale/sharing opt-out signals
3. **Other Regions**: No restrictions applied

When consent is denied or user opts out, the SDK removes PII from ad requests:

* Advertising ID (IDFA) is cleared
* Geo coordinates (lat/lon) are removed
* User key-values are not sent
* Hashed user ID is excluded

#### Supported Privacy Keys

| Key                    | Standard                                                                                          | Description                                                        |
| ---------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `IABGPP_HDR_GppString` | [GPP](https://github.com/InteractiveAdvertisingBureau/Global-Privacy-Platform)                    | Global Privacy Platform string (modern)                            |
| `IABGPP_GppSID`        | GPP                                                                                               | Section IDs (e.g., "2" for EU, "7" for US-National, "8" for US-CA) |
| `IABTCF_TCString`      | [TCF v2](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework) | GDPR consent string (legacy)                                       |
| `IABTCF_gdprApplies`   | TCF v2                                                                                            | Whether GDPR applies (1 = yes, 0 = no)                             |
| `IABUSPrivacy_String`  | [US Privacy](https://github.com/InteractiveAdvertisingBureau/USPrivacy)                           | CCPA privacy string (legacy, e.g., "1YNN")                         |

<Note>
  The SDK prioritizes GPP (modern standard) over legacy TCF/US Privacy strings when both are available.
</Note>

#### App Tracking Transparency (ATT)

On iOS 14.5+, you must request App Tracking Transparency authorization before the SDK can access the IDFA. Request ATT permission before initializing the CloudX SDK:

<CodeGroup>
  ```objc Objective-C theme={null}
  #import <AppTrackingTransparency/AppTrackingTransparency.h>

  if (@available(iOS 14.5, *)) {
      [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
          // Initialize CloudX SDK after ATT response
          [self initializeCloudX];
      }];
  } else {
      [self initializeCloudX];
  }
  ```

  ```swift Swift theme={null}
  import AppTrackingTransparency

  if #available(iOS 14.5, *) {
      ATTrackingManager.requestTrackingAuthorization { status in
          // Initialize CloudX SDK after ATT response
          self.initializeCloudX()
      }
  } else {
      initializeCloudX()
  }
  ```
</CodeGroup>

Add the `NSUserTrackingUsageDescription` key to your Info.plist with a description of why you need tracking permission.

#### Manual Privacy API

If you manage user consent yourself (without a CMP), you can set GDPR and CCPA privacy status directly. **Call these before initializing the SDK** — some ad network SDKs require privacy settings at initialization time and will not apply values set after init.

<CodeGroup>
  ```objc Objective-C theme={null}
  // Set privacy BEFORE initializing the SDK
  [CloudXCore setHasUserConsent:@YES];
  [CloudXCore setDoNotSell:@NO];

  [[CloudXCore shared] initializeWithConfiguration:config completion:completion];
  ```

  ```swift Swift theme={null}
  // Set privacy BEFORE initializing the SDK
  CloudXCore.setHasUserConsent(true)
  CloudXCore.setDoNotSell(false)

  CloudXCore.shared.initialize(with: config) { sdkConfig, error in
      // ...
  }
  ```
</CodeGroup>

<Note>
  When both manual values and CMP signals are present, CMP signals (GPP/TCF/US Privacy) take priority. Manual values act as a fallback when no CMP is integrated. Pass `nil` to clear manual values and defer entirely to your CMP.
</Note>

### User Targeting

<CodeGroup>
  ```objc Objective-C theme={null}
  // Set hashed user ID for targeting
  [[CloudXCore shared] setHashedUserID:@"hashed-user-id"];

  // Set custom user key-value pairs (cleared by privacy regulations)
  [[CloudXCore shared] setUserKeyValue:@"age" value:@"25"];
  [[CloudXCore shared] setUserKeyValue:@"gender" value:@"male"];
  [[CloudXCore shared] setUserKeyValue:@"location" value:@"US"];

  // Set custom app key-value pairs (NOT affected by privacy regulations)
  [[CloudXCore shared] setAppKeyValue:@"app_version" value:@"1.0.0"];
  [[CloudXCore shared] setAppKeyValue:@"user_level" value:@"premium"];

  // Clear all custom key-values
  [[CloudXCore shared] clearAllKeyValues];
  ```

  ```swift Swift theme={null}
  // Set hashed user ID for targeting
  CloudXCore.shared.setHashedUserID("hashed-user-id")

  // Set custom user key-value pairs (cleared by privacy regulations)
  CloudXCore.shared.setUserKeyValue("age", value: "25")
  CloudXCore.shared.setUserKeyValue("gender", value: "male")
  CloudXCore.shared.setUserKeyValue("location", value: "US")

  // Set custom app key-value pairs (NOT affected by privacy regulations)
  CloudXCore.shared.setAppKeyValue("app_version", value: "1.0.0")
  CloudXCore.shared.setAppKeyValue("user_level", value: "premium")

  // Clear all custom key-values
  CloudXCore.shared.clearAllKeyValues()
  ```
</CodeGroup>

## Support

For support, contact [mobile@cloudx.io](mailto:mobile@cloudx.io)
