Skip to main content

CloudX iOS SDK

Requires iOS 14.0+ and Xcode 12.0+.

Installation

CocoaPods

Podfile
platform :ios, '14.0'

target 'YourApp' do
  use_frameworks!

  # Core SDK
  pod 'CloudXCore'

  # Adapters for ad networks (add as needed)
  pod 'CloudXMetaAdapter'       # Meta Audience Network 6.21.0
  pod 'CloudXVungleAdapter'     # Vungle SDK 7.6.0
  pod 'CloudXInMobiAdapter'     # InMobi SDK 11.1.0
end
pod install --repo-update

Initialization

#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);
    }
}];

Ad Integration

@interface YourViewController () <CLXBannerDelegate, CLXAdRevenueDelegate>
@property (nonatomic, strong) CLXBannerAdView *bannerAd;
@end

@implementation YourViewController

- (void)createBannerAd {
    self.bannerAd = [[CloudXCore shared] createBannerWithAdUnitId:@"your-banner-ad-unit-id"
                                                   viewController:self];
    self.bannerAd.delegate = self;
    self.bannerAd.revenueDelegate = self;

    if (self.bannerAd) {
        self.bannerAd.translatesAutoresizingMaskIntoConstraints = NO;
        [self.view addSubview:self.bannerAd];

        [NSLayoutConstraint activateConstraints:@[
            [self.bannerAd.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
            [self.bannerAd.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
            [self.bannerAd.widthAnchor constraintEqualToConstant:320],
            [self.bannerAd.heightAnchor constraintEqualToConstant:50]
        ]];

        [self.bannerAd load];
    }
}

- (void)dealloc {
    [self.bannerAd destroy];
}

#pragma mark - CLXBannerDelegate

- (void)didLoadAd:(CLXAd *)ad {
    NSLog(@"Banner ad loaded from %@", ad.networkName);
}

- (void)didFailToLoadAd:(NSString *)adUnitId error:(CLXError *)error {
    NSLog(@"Banner ad failed to load: %@", error.localizedDescription);
}

- (void)didClickAd:(CLXAd *)ad {
    NSLog(@"Banner ad clicked");
}

// Optional: Called when banner expands (e.g., MRAID)
- (void)didExpandAd:(CLXAd *)ad {
    NSLog(@"Banner ad expanded");
}

// Optional: Called when banner collapses
- (void)didCollapseAd:(CLXAd *)ad {
    NSLog(@"Banner ad collapsed");
}

#pragma mark - CLXAdRevenueDelegate

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

@end
Banner ads auto-refresh by default. To control refresh manually:
[self.bannerAd stopAutoRefresh];    // Stop auto-refresh
[self.bannerAd load];               // Manually load a new ad
[self.bannerAd startAutoRefresh];   // Re-enable auto-refresh
Optional placement and custom data for tracking:
self.bannerAd.placement = @"home_screen";
self.bannerAd.customData = @"level:5,coins:100";

MREC Ads (300x250)

@interface YourViewController () <CLXBannerDelegate, CLXAdRevenueDelegate>
@property (nonatomic, strong) CLXBannerAdView *mrecAd;
@end

@implementation YourViewController

- (void)createMRECAd {
    self.mrecAd = [[CloudXCore shared] createMRECWithAdUnitId:@"your-mrec-ad-unit-id"
                                               viewController:self];
    self.mrecAd.delegate = self;
    self.mrecAd.revenueDelegate = self;

    if (self.mrecAd) {
        self.mrecAd.translatesAutoresizingMaskIntoConstraints = NO;
        [self.view addSubview:self.mrecAd];

        [NSLayoutConstraint activateConstraints:@[
            [self.mrecAd.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
            [self.mrecAd.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-20],
            [self.mrecAd.widthAnchor constraintEqualToConstant:300],
            [self.mrecAd.heightAnchor constraintEqualToConstant:250]
        ]];

        [self.mrecAd load];
    }
}

- (void)dealloc {
    [self.mrecAd destroy];
}

#pragma mark - CLXBannerDelegate

- (void)didLoadAd:(CLXAd *)ad {
    NSLog(@"MREC ad loaded from %@", ad.networkName);
}

- (void)didFailToLoadAd:(NSString *)adUnitId error:(CLXError *)error {
    NSLog(@"MREC ad failed to load: %@", error.localizedDescription);
}

- (void)didClickAd:(CLXAd *)ad {
    NSLog(@"MREC ad clicked");
}

// Optional: Called when MREC expands (e.g., MRAID)
- (void)didExpandAd:(CLXAd *)ad {
    NSLog(@"MREC ad expanded");
}

// Optional: Called when MREC collapses
- (void)didCollapseAd:(CLXAd *)ad {
    NSLog(@"MREC ad collapsed");
}

#pragma mark - CLXAdRevenueDelegate

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

@end
MREC ads also auto-refresh by default. Use the same refresh control methods as Banner ads.

Interstitial Ads

@interface YourViewController () <CLXInterstitialDelegate, CLXAdRevenueDelegate>
@property (nonatomic, strong) CLXInterstitial *interstitialAd;
@end

@implementation YourViewController

- (void)createInterstitialAd {
    self.interstitialAd = [[CloudXCore shared] createInterstitialWithAdUnitId:@"your-interstitial-ad-unit-id"];
    self.interstitialAd.delegate = self;
    self.interstitialAd.revenueDelegate = self;
    [self.interstitialAd load];
}

- (void)showInterstitialAd {
    if (self.interstitialAd.isReady) {
        // Basic show
        [self.interstitialAd showFromViewController:self];

        // Or with optional placement and custom data for tracking
        // [self.interstitialAd showFromViewController:self placement:@"level_complete" customData:@"level:5,score:1000"];
    } else {
        NSLog(@"Interstitial ad not ready");
    }
}

- (void)dealloc {
    [self.interstitialAd destroy];
}

#pragma mark - CLXInterstitialDelegate

- (void)didLoadAd:(CLXAd *)ad {
    NSLog(@"Interstitial ad loaded from %@", ad.networkName);
}

- (void)didFailToLoadAd:(NSString *)adUnitId error:(CLXError *)error {
    NSLog(@"Interstitial ad failed to load: %@", error.localizedDescription);
}

- (void)didDisplayAd:(CLXAd *)ad {
    NSLog(@"Interstitial ad displayed");
}

- (void)didFailToDisplayAd:(CLXAd *)ad error:(CLXError *)error {
    NSLog(@"Interstitial ad failed to display: %@", error.localizedDescription);
}

- (void)didHideAd:(CLXAd *)ad {
    NSLog(@"Interstitial ad hidden");
    [self createInterstitialAd]; // Reload for next use
}

- (void)didClickAd:(CLXAd *)ad {
    NSLog(@"Interstitial ad clicked");
}

#pragma mark - CLXAdRevenueDelegate

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

@end

Rewarded Ads

@interface YourViewController () <CLXRewardedDelegate, CLXAdRevenueDelegate>
@property (nonatomic, strong) CLXRewarded *rewardedAd;
@end

@implementation YourViewController

- (void)createRewardedAd {
    self.rewardedAd = [[CloudXCore shared] createRewardedWithAdUnitId:@"your-rewarded-ad-unit-id"];
    self.rewardedAd.delegate = self;
    self.rewardedAd.revenueDelegate = self;
    [self.rewardedAd load];
}

- (void)showRewardedAd {
    if (self.rewardedAd.isReady) {
        // Basic show
        [self.rewardedAd showFromViewController:self];

        // Or with optional placement and custom data for tracking
        // [self.rewardedAd showFromViewController:self placement:@"bonus_coins" customData:@"level:5,coins:100"];
    } else {
        NSLog(@"Rewarded ad not ready");
    }
}

- (void)dealloc {
    [self.rewardedAd destroy];
}

#pragma mark - CLXRewardedDelegate

- (void)didLoadAd:(CLXAd *)ad {
    NSLog(@"Rewarded ad loaded from %@", ad.networkName);
}

- (void)didFailToLoadAd:(NSString *)adUnitId error:(CLXError *)error {
    NSLog(@"Rewarded ad failed to load: %@", error.localizedDescription);
}

- (void)didDisplayAd:(CLXAd *)ad {
    NSLog(@"Rewarded ad displayed");
}

- (void)didFailToDisplayAd:(CLXAd *)ad error:(CLXError *)error {
    NSLog(@"Rewarded ad failed to display: %@", error.localizedDescription);
}

- (void)didHideAd:(CLXAd *)ad {
    NSLog(@"Rewarded ad hidden");
    [self createRewardedAd]; // Reload for next use
}

- (void)didClickAd:(CLXAd *)ad {
    NSLog(@"Rewarded ad clicked");
}

- (void)didRewardUserForAd:(CLXAd *)ad withReward:(CLXReward *)reward {
    NSLog(@"User rewarded: %ld %@", (long)reward.amount, reward.label);
    // Grant the reward to the user
}

#pragma mark - CLXAdRevenueDelegate

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

@end

Ad Information (CLXAd)

The CLXAd object is passed to delegate callbacks and contains information about the loaded/displayed ad:
PropertyTypeDescription
adFormatCLXAdFormatAd format (banner, mrec, interstitial, rewarded)
adUnitIdNSString?The ad unit ID
adUnitNameNSString?The ad unit name
networkNameNSString?Name of the winning ad network
networkPlacementNSString?Network-specific placement ID
placementNSString?Custom placement set via placement property
revenueNSNumber?Impression-level revenue in USD
- (void)didLoadAd:(CLXAd *)ad {
    NSLog(@"Ad format: %ld", (long)ad.adFormat);
    NSLog(@"Network: %@", ad.networkName);
    NSLog(@"Revenue: %@", ad.revenue);
}

Error Handling

All SDK errors are returned as CLXError objects in delegate callbacks:
PropertyTypeDescription
codeCLXErrorCodeError category
localizedDescriptionNSStringHuman-readable description
underlyingErrorNSError?Optional underlying error

Error Code Categories

RangeCategoryCommon Codes
0GeneralCLXErrorCodeInternalError
100-199NetworkCLXErrorCodeNetworkError, CLXErrorCodeNetworkTimeout, CLXErrorCodeServerError, CLXErrorCodeNoConnection
200-299InitializationCLXErrorCodeNotInitialized, CLXErrorCodeSDKDisabled, CLXErrorCodeNoAdaptersFound, CLXErrorCodeInvalidAppKey
300-399Ad LoadingCLXErrorCodeNoFill, CLXErrorCodeInvalidAdUnit, CLXErrorCodeAdsDisabled
400-499DisplayCLXErrorCodeAdNotReady, CLXErrorCodeAdAlreadyShowing
600-699AdapterCLXErrorCodeAdapterNoFill, CLXErrorCodeAdapterTimeout, CLXErrorCodeAdapterLoadTimeout, CLXErrorCodeAdapterInitializationError

Advanced Features

Debug Logging

[CloudXCore setMinLogLevel:CLXLogLevelDebug];  // Enable debug logging
[CloudXCore setMinLogLevel:CLXLogLevelNone];   // Disable all logging
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.
self.bannerAd.revenueDelegate = self;

- (void)didPayRevenueForAd:(CLXAd *)ad {
    NSLog(@"Revenue: %@ from %@", ad.revenue, ad.networkName);
}
Works with all ad formats (banner, MREC, interstitial, rewarded).

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
Test mode is determined by the server, so you don’t need to change any code between development and production builds.

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-4 and vendor consent (CloudX Vendor ID: 1510)
  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

KeyStandardDescription
IABGPP_HDR_GppStringGPPGlobal Privacy Platform string (modern)
IABGPP_GppSIDGPPSection IDs (e.g., “2” for EU, “7” for US-National, “8” for US-CA)
IABTCF_TCStringTCF v2GDPR consent string (legacy)
IABTCF_gdprAppliesTCF v2Whether GDPR applies (1 = yes, 0 = no)
IABUSPrivacy_StringUS PrivacyCCPA privacy string (legacy, e.g., “1YNN”)
The SDK prioritizes GPP (modern standard) over legacy TCF/US Privacy strings when both are available.

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:
#import <AppTrackingTransparency/AppTrackingTransparency.h>

if (@available(iOS 14.5, *)) {
    [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
        // Initialize CloudX SDK after ATT response
        [self initializeCloudX];
    }];
} else {
    [self initializeCloudX];
}
Add the NSUserTrackingUsageDescription key to your Info.plist with a description of why you need tracking permission.

User Targeting

// 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];

Support

For support, contact mobile@cloudx.io