Implement a Custom GMA Adapter for iOS
LiveRamp’s integration with Google's Secure Signals lets publishers pass RampID envelopes as signals to Google Ad Manager’s (GAM) Open Bidding. On mobile applications, the Google Mobile Ads (GMA) SDK collects those signals through classes that conform to the GADRTBAdapter protocol.
This article uses the following placeholders in code examples which you should define yourself as part of the adapter creation process:
YourRtbAdapter: Replace with your own adapter class name (e.g. AcmeMediationAdapter, PublisherRtbAdapter)YourEnvelopeManager: Replace with yourenvelopehelper.
Prerequisites
Before starting, confirm the following are in place:
- ATS API integration is complete: Your app retrieves, refreshes, and caches identity envelopes independently. The adapter only reads from this layer and not call LiveRamp APIs itself. See “ATS API Implementation Guide for Mobile Publishers” for more information.
- Google Mobile Ads SDK is integrated: Your app already initializes
MobileAdsand loads ads. - GAM access is available: You or your ad ops team can edit yield groups and custom events to register the adapter class name and enable Secure Signals for LiveRamp. See Google's Share secure signals with bidders.
- Agree on a class name: The adapter class name registered in the Google Ad Manager UI must match with what's implemented in your code. Work with the ad ops team to be aligned on a class name before implementation.
Overall Steps
- Choose and register your adapter class name in GAM.
- Implement the adapter class in your app.
- Update your app dependencies.
- Make sure envelope is present at ad time.
- Validate the adapter on device.
Migrating From the ATS Mobile SDK
If you have previously leveraged LRAtsMediationAdapter as part of the ATS Mobile SDK, you must:
- Replace the adapter with your own
GADRTBAdapterclass. - Replace the
LRAtsMediationAdapterclass name in xcframework to the new class name in your app.
Refer to the table below to see how LRAtsMediationAdapter compares with your own adapter.
| LRAtsMediationAdapter | Your RTB Adapter | |
|---|---|---|
| Method | [[LRAts shared] status] == LRStatusReady in setUpWithConfiguration | completionHandler(nil) only |
| Envelope supply | [LRAts shared] getEnvelopeWithCompletion: | YourEnvelopeManager getEnvelopeWithCompletion: |
| Signal priority | envelope27 then envelope then "" | Same priority in collectSignalsForRequestParameters |
| Error | NSError code 101 on failure | NSError code 101 on failure |
| Where it's shipped | LiveRamp's xcframework | Your app target; class name registered in GAM |
1. Choose and Register Your Adapter Class Name in GAM
GAM discovers your adapter by the class name you register in the Ad Manager UI. Engineering and ad ops must agree on the class name before implementation, because the string in GAM and the class name in your app must match exactly.
Work with your ad ops team to complete these steps in Google Ad Manager:
- In GAM, select Admin → Yield groups (or your existing yield group setup for these ad units).
- Add or edit the Custom event used for this RTB/identity signal.
- In the Class name field, enter your adapter class name exactly as you will define it in code—for example,
YourRtbAdapter. - Enter a Label that identifies this signal for your team.
- Leave Parameter empty unless your implementation requires it.
- Save and publish.
See Google’s article “Create and manage yield groups” for more information.
If you previously used the LiveRamp ATS SDK, update every yield group, mediation or custom event settings that referenced to the old
LRAtsMediationAdapterclass name and replace it with your new class name.
| Handoff | Owner | Value |
|---|---|---|
| Adapter class name | Engineering | The chosen class name. For example, YourRtbAdapter |
| Class name field in Google Ad Manager | Ad ops | Same string as the adapter class name. |
| Ad units | Ad ops | Units tied to this yield group |
2. Implement the Adapter Class
Create a new class in your app target—not in a separate library or framework. The class must conform to Google's GADRTBAdapter protocol, using the class name you registered in GAM UI from the previous step.
At runtime, GAM calls your adapter in two stages:
- Initialization — When
GADMobileAds.sharedInstance().start()is called, the GMA SDK loads your adapter by class name and callssetUpWithConfigurationonce. - Per-bid signal collection — When an ad loads, the GMA SDK calls
collectSignalsForRequestParameters. Your adapter reads the current envelope and returns it viacompletionHandler. GAM attaches the signal to the outgoing bid request.
If collectSignalsForRequestParameters is called before an envelope is available, return an error via completionHandler. Do not use setUpWithConfiguration to validate envelope state.
For details on the methods you implement, see the table below:
| Method | Purpose |
|---|---|
+setUpWithConfiguration:completionHandler: | Called once when the Mobile Ads SDK initializes to report success so GAM can use your adapter for later requests. No SDK ready-state check required. |
-collectSignalsForRequestParameters:completionHandler: | Called before each bid. Returns envelope27, or envelope (type 19) or “” via completionHandler. Passes an error if no envelope is available. |
+adSDKVersion / +adapterVersion | Returns a GADVersionNumber used for reporting and debugging. |
+networkExtrasClass | Returns Nil if you do not use network extras. |
Signal priority
When returning the envelope string, apply this priority order:
envelope27if presentenvelope(type 19) ifenvelope27is absent- Empty string
""if neither is present - On error, call
completionHandler(nil, error)
Objective-C
Create YourRTBAdapter.m in your app target. Rename YourRtbAdapter and YourEnvelopeManager in the implementation reference below to fit your project before use.
YourRtbAdapter.m (filename = your class name)
// YourRtbAdapter.h
#import <Foundation/Foundation.h>
#import <GoogleMobileAds/GoogleMobileAds.h>
@interface YourRtbAdapter : NSObject <GADRTBAdapter>
@end
// YourRtbAdapter.m
#import "YourRtbAdapter.h"
#import "YourEnvelopeManager.h"
@implementation YourRtbAdapter
+ (GADVersionNumber)adSDKVersion {
return (GADVersionNumber){1, 0, 0};
}
+ (GADVersionNumber)adapterVersion {
return (GADVersionNumber){1, 0, 0};
}
+ (nullable Class<GADAdNetworkExtras>)networkExtrasClass {
return Nil;
}
+ (void)setUpWithConfiguration:(GADMediationServerConfiguration *)configuration
completionHandler:(GADMediationAdapterSetUpCompletionBlock)completionHandler {
completionHandler(nil);
}
- (void)collectSignalsForRequestParameters:(GADRTBRequestParameters *)params
completionHandler:(GADRTBSignalCompletionHandler)completionHandler {
[YourEnvelopeManager getEnvelopeWithCompletion:^(YourEnvelope *envelope, NSError *error) {
if (envelope != nil) {
NSString *signal = envelope.envelope27 ?: (envelope.envelope ?: @"");
completionHandler(signal, nil);
} else {
NSString *errorMsg = error.localizedDescription ?: @"Unable to return envelope.";
NSError *customError = [NSError errorWithDomain:@"com.yourcompany.yourapp" code:101
userInfo:@{NSLocalizedDescriptionKey : errorMsg}];
completionHandler(nil, customError);
}
}];
}
@end
Swift
Create YourRtbAdapter.swift in your app target. Rename YourRtbAdapter and YourEnvelopeManager in the implementation reference below to fit your project before use.
import GoogleMobileAds
@objc(YourRtbAdapter)
class YourRtbAdapter: NSObject, GADRTBAdapter {
static func setUp(with configuration: GADMediationServerConfiguration,
completionHandler: @escaping GADMediationAdapterSetUpCompletionBlock) {
// No SDK ready-state check required.
completionHandler(nil)
}
static func adSDKVersion() -> GADVersionNumber {
GADVersionNumber(majorVersion: 1, minorVersion: 0, patchVersion: 0)
}
static func adapterVersion() -> GADVersionNumber { adSDKVersion() }
static func networkExtrasClass() -> GADAdNetworkExtras.Type? { nil }
func collectSignals(for params: GADRTBRequestParameters,
completionHandler: @escaping GADRTBSignalCompletionHandler) {
YourEnvelopeManager.getEnvelope { envelope, error in
if let envelope = envelope {
completionHandler(envelope.envelope27 ?? envelope.envelope ?? "", nil)
} else {
let msg = error?.localizedDescription ?? "Unable to return envelope."
completionHandler(nil, NSError(domain: "com.yourcompany.yourapp",
code: 101,
userInfo: [NSLocalizedDescriptionKey: msg]))
}
}
}
}3. Update App Dependencies
Remove the LiveRamp ATS SDK and keep only the Google Mobile Ads SDK.
CocoaPods
# Keep
pod 'Google-Mobile-Ads-SDK', '~> 12.0'
# Remove
# pod 'LRAtsSDK'
# pod 'LRAtsSDKMediationAdapter'
Swift Package Manager
Remove any LRAts packages from your Package.swift file and Xcode project dependencies. Keep the GoogleMobileAds package.
Do not declare your adapter in
Info.plist. GAM loads the adapter by class name at runtime; no additional registration is needed.
4. Make Sure Envelope is Present at Ad Time
The adapter does not call LiveRamp APIs itself to retrieve or refresh envelopes. It only reads whatever your envelope layer provides at the time GAM calls collectSignalsForRequestParameters.
Because of this, your ATS API implementation should be properly configured to:
- Retrieve envelopes for consented identifiers
- Refresh envelopes when needed before an ad load
- Supply the envelope string at
collectSignalsForRequestParameters
Call following ATS API endpoints to retrieve and refresh endpoints:
| Action | Endpoint |
|---|---|
| Retrieve | https://api.rlcdn.com/api/identity/v2/envelope/ |
| Refresh | https://api.rlcdn.com/api/identity/v2/envelope/refresh/ |
For more information, see "ATS API Implementation Guide for Mobile Publishers".
5. Verify the Adapter on Device
After implementing the class, confirm that GAM recognizes your adapter at runtime.
GADMobileAds.sharedInstance().start { status in
let className = "YourRtbAdapter" // Replace with your actual class name
if let adapterStatus = status.adapterStatusesByClassName[className] {
print("Adapter status: \(adapterStatus.state.rawValue)")
}
}
// After an ad loads:
print("Mediation class: \(ad.responseInfo?.adNetworkClassName ?? "none")")You should see your class name in the output. If you see LRAtsMediationAdapter instead, the old SDK is still linked or GAM still points to the old class name.
Validation checklist
- [ ] Adapter class name agreed on and registered in the GAM Class name field
- [ ] Old
LRAtsMediationAdapterremoved from GAM configuration - [ ] LiveRamp SDK (
LRAtsSDK/LRAtsSDKMediationAdapter) removed from CocoaPods or SPM - [ ] Your
GADRTBAdapterclass added to your app target with the same class name as registered in GAM - [ ]
setUpWithConfigurationalways callscompletionHandler(nil)— no SDK ready-state check - [ ]
collectSignalsForRequestParameterspassesenvelope27→envelope19→""in that priority order - [ ]
adapterStatusesByClassNameshows your class name afterGADMobileAds.sharedInstance().start() - [ ] Envelope is available from your direct API flow before or during ad load