广告收入归因
追踪来自聚合平台的广告收入,并将其归因到获取用户的营销活动,从而提供涵盖 活动成本、应用内购买和广告变现的完整 ROI 可见性。
概述
什么是广告收入归因
广告收入归因将移动应用广告收入与推动应用安装的用户获取活动相连接, 使您能够衡量包含广告变现在内的真实活动盈利能力。
主要优势:
- 统一的 ROI 视图: 在单一控制台中查看活动成本、应用内收入 和广告收入
- 活动优化: 将广告收入数据回传给广告网络,以改进 竞价和定向
- LTV 衡量: 计算包含广告变现在内的用户完整生命周期价值
数据来源: 广告收入数据通常来自您的聚合平台(例如 AdMob、AppLovin MAX、IronSource), 以用户级别或展示级别提供。Singular 支持多种集成方式来接收此数据。
了解更多: 有关设置、报告和故障排除的完整详情,请参阅 广告收入归因常见问题 。
实施要求
关键准则
必填的 SingularAdData 参数:
构造函数参数
adPlatform、
withCurrency 和
withRevenue
均为必填项。如果任一项缺失、为空或无效(依据
-hasRequiredParams 判定),
SDK 会静默丢弃对
+adRevenue:
的调用,且不会抛出异常。在 SDK 未启动时,该调用也会静默无效,因此在调用前请始终确认
+start:
已运行。
使用正确的构造函数签名:
-initWithAdPlatform:withCurrency:withRevenue:
是受支持的构造函数。旧版的
-initWithAdPlatfrom:
(注意拼写错误)被标注为
__attribute__((deprecated))
,会产生编译器警告。请勿在新代码中使用。
数据精度至关重要:
- 货币代码: 使用三位字母的 ISO 4217 货币代码 (例如 USD、EUR、INR)。许多聚合平台以 USD 报告, 请在实施前验证您平台的货币
-
发送前验证:
在调用
Singular.adRevenue()之前,请始终验证收入和货币数据。 错误的数据提交后无法更正 - 平台差异: 与使用 micros 的 Android 不同,iOS 直接以标准货币单位 (例如 $0.005)接收收入。请始终查看您平台的文档以确定正确的格式
设置步骤
请按照以下步骤实施广告收入归因:
- 更新 SDK: 确保您使用的是最新版本的 Singular SDK
- 选择集成方式: 选择下方与您的设置相匹配的聚合平台集成
- 实现回调: 添加平台特定的 paid 事件监听器以捕获收入数据
- 验证数据: 测试收入报告,并验证数据已显示在 Singular 控制台中
平台集成
AdMob 集成
使用 paid 事件监听器追踪来自 Google AdMob 的广告收入,以实现展示级别的 收入报告。
要求:
平台收入报告: AdMob 在不同平台上以不同方式报告收入。iOS 以标准货币单位 返回收入(例如 $0.005 = 0.005)。请将该值直接发送给 Singular,无需转换。
实现
在加载广告时设置 paid 事件处理程序,以捕获收入数据并将其发送给 Singular。
import Singular
import GoogleMobileAds
private let adUnitID = "AD_UNIT_ID"
var rewardedAd: GADRewardedAd?
func loadRewardedAd() {
let request = GADRequest()
GADRewardedAd.load(withAdUnitID: adUnitID, request: request) { [weak self] ad, error in
guard let self = self else { return }
if let error = error {
print("Rewarded ad failed to load: \(error.localizedDescription)")
return
}
self.rewardedAd = ad
// Set paid event handler for revenue tracking
self.rewardedAd?.paidEventHandler = { adValue in
// Extract revenue and currency from AdValue
let revenue = adValue.value.doubleValue
let currency = adValue.currencyCode
// Validate revenue and currency before sending
guard revenue > 0, let currency = currency, !currency.isEmpty else {
print("Invalid ad revenue data: revenue = \(revenue), currency = \(String(describing: currency))")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "AdMob",
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) \(currency)")
}
}
}
#import <Singular/Singular.h>
#import <GoogleMobileAds/GoogleMobileAds.h>
static NSString *const adUnitID = @"AD_UNIT_ID";
@interface AdManager ()
@property(nonatomic, strong) GADRewardedAd *rewardedAd;
@end
@implementation AdManager
- (void)loadRewardedAd {
GADRequest *request = [[GADRequest alloc] init];
[GADRewardedAd loadWithAdUnitID:adUnitID
request:request
completionHandler:^(GADRewardedAd *ad, NSError *error) {
if (error) {
NSLog(@"Rewarded ad failed to load: %@", error.localizedDescription);
return;
}
self.rewardedAd = ad;
// Set paid event handler for revenue tracking
self.rewardedAd.paidEventHandler = ^(GADAdValue *adValue) {
// Extract revenue and currency from AdValue
double revenue = adValue.value.doubleValue;
NSString *currency = adValue.currencyCode;
// Validate revenue and currency before sending
if (revenue > 0 && currency.length > 0) {
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"AdMob"
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f %@", revenue, currency);
} else {
NSLog(@"Invalid ad revenue data: revenue = %f, currency = %@",
revenue, currency);
}
};
}];
}
@end
AppLovin MAX 集成
使用 AppLovin 的 Impression-Level User Revenue API 分享展示级别的广告收入。
要求:
- 实现 AppLovin MAX SDK(参见 Impression-Level Revenue API 指南 )
-
通过 AppLovin Communicator 订阅
max_revenue_events主题
实现
通过 AppLovin Communicator 回调处理收入消息。
import Singular
import AppLovinSDK
func didReceive(_ message: ALCMessage) {
// Check for revenue events topic
if message.topic == "max_revenue_events" {
// Extract and validate revenue value
guard let revenueValue = message.data["revenue"] as? Double,
revenueValue > 0 else {
print("Failed to parse valid revenue value or revenue is not greater than 0")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "AppLovin",
currency: "USD", // AppLovin typically reports in USD
revenue: revenueValue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenueValue) USD")
}
}
#import <Singular/Singular.h>
#import <AppLovinSDK/AppLovinSDK.h>
- (void)didReceive:(ALCMessage *)message {
// Check for revenue events topic
if ([@"max_revenue_events" isEqualToString:message.topic]) {
NSDictionary *data = message.data;
// Extract and validate revenue value
NSNumber *revenueNumber = data[@"revenue"];
double revenueValue = [revenueNumber doubleValue];
if (revenueValue > 0) {
// Create ad revenue data object
SingularAdData *adData = [[SingularAdData alloc]
initWithAdPlatform:@"AppLovin"
currency:@"USD"
revenue:revenueValue];
// Send to Singular
[Singular adRevenue:adData];
NSLog(@"Ad revenue sent: %f USD", revenueValue);
} else {
NSLog(@"Failed to parse valid revenue value or revenue is not greater than 0");
}
}
}
Unity LevelPlay (IronSource) 集成
使用 IronSource SDK 追踪来自 ironSource 和聚合网络的展示级别收入。
要求:
- 实现 ironSource SDK(参见 入门指南 )
- 在您的 IronSource 控制台中启用 ARM SDK Postbacks 标志
- 设置 impression data 监听器以接收收入回调
了解更多: 有关完整的设置详情,请参阅 IronSource 广告收入文档 。
实现
实现 impression data success 回调以捕获并发送收入。
import Singular
import IronSource
func impressionDataDidSucceed(_ impressionData: ISImpressionData?) {
// Validate impression data
guard let impressionData = impressionData else {
print("No impression data available")
return
}
// Extract and validate revenue
let revenue = impressionData.revenue
guard revenue > 0 else {
print("Invalid revenue value: \(revenue)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "IronSource",
currency: "USD", // IronSource typically reports in USD
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) USD")
}
#import <Singular/Singular.h>
#import <IronSource/IronSource.h>
- (void)impressionDataDidSucceed:(ISImpressionData *)impressionData {
// Validate impression data
if (!impressionData) {
NSLog(@"No impression data available");
return;
}
// Extract and validate revenue
double revenue = impressionData.revenue;
if (revenue <= 0) {
NSLog(@"Invalid revenue value: %f", revenue);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"IronSource"
currency:@"USD"
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f USD", revenue);
}
TradPlus 集成
使用 impression delegate 捕获来自 TradPlus 聚合的广告收入。
要求:
-
通过
TradPlus.sharedInstance().impressionDelegate设置 impression delegate -
处理
tradPlusAdImpression回调以接收收入数据 - 将 eCPM 从毫单位转换为标准货币单位(除以 1000)
实现
注册一个 impression delegate 以追踪所有广告展示和收入。
import Singular
import TradPlusSDK
// Set up the delegate
TradPlus.sharedInstance().impressionDelegate = self
// Delegate method for handling ad impressions
func tradPlusAdImpression(_ adInfo: [String: Any]) {
let currency = "USD"
// Extract and validate eCPM value
guard let ecpmValue = adInfo["ecpm"] as? NSNumber else {
print("No eCPM data available in adInfo")
return
}
// Convert eCPM from milli-units to dollars
let revenue = ecpmValue.doubleValue / 1000.0
// Validate revenue
guard revenue > 0 else {
print("Ad Revenue value out of expected range: \(revenue)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: "TradPlus",
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Ad revenue sent: \(revenue) \(currency)")
}
#import <Singular/Singular.h>
#import <TradPlusSDK/TradPlusSDK.h>
// Set up the delegate
TradPlus.sharedInstance.impressionDelegate = self;
// Delegate method for handling ad impressions
- (void)tradPlusAdImpression:(NSDictionary<NSString *, id> *)adInfo {
NSString *currency = @"USD";
// Extract and validate eCPM value
NSNumber *ecpmValue = adInfo[@"ecpm"];
if (!ecpmValue) {
NSLog(@"No eCPM data available in adInfo");
return;
}
// Convert eCPM from milli-units to dollars
double revenue = [ecpmValue doubleValue] / 1000.0;
// Validate revenue
if (revenue <= 0) {
NSLog(@"Ad Revenue value out of expected range: %f", revenue);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"TradPlus"
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Ad revenue sent: %f %@", revenue, currency);
}
通用集成(其他平台)
使用通用的
SingularAdData
接口集成任何聚合平台。
要求:
- 可以访问聚合平台的展示级别收入数据
- 标准货币单位的收入金额
- ISO 4217 货币代码(例如 USD、EUR、INR)
数据精度: 在发送给 Singular 之前,请验证收入和货币数据。 错误的数据提交后无法更正。
实现
使用平台名称、货币和收入创建
SingularAdData
对象,然后调用
Singular.adRevenue()
。
import Singular
func reportAdRevenue(adPlatform: String, currency: String, revenue: Double) {
// Validate revenue
guard revenue > 0 else {
print("Invalid revenue value: \(revenue)")
return
}
// Validate currency
guard !currency.isEmpty else {
print("Invalid currency: \(currency)")
return
}
// Create ad revenue data object
let data = SingularAdData(
adPlatform: adPlatform,
currency: currency,
revenue: revenue
)
// Send to Singular
Singular.adRevenue(data)
print("Revenue sent: \(revenue) \(currency) from \(adPlatform)")
}
// Example usage
reportAdRevenue(adPlatform: "MyMediationPlatform", currency: "USD", revenue: 0.05)
#import <Singular/Singular.h>
- (void)reportAdRevenueWithPlatform:(NSString *)adPlatform
currency:(NSString *)currency
revenue:(double)revenue {
// Validate revenue
if (revenue <= 0) {
NSLog(@"Invalid revenue value: %f", revenue);
return;
}
// Validate currency
if (!currency || currency.length == 0) {
NSLog(@"Invalid currency: %@", currency);
return;
}
// Create ad revenue data object
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:adPlatform
currency:currency
revenue:revenue];
// Send to Singular
[Singular adRevenue:data];
NSLog(@"Revenue sent: %f %@ from %@", revenue, currency, adPlatform);
}
// Example usage
[self reportAdRevenueWithPlatform:@"MyMediationPlatform"
currency:@"USD"
revenue:0.05];
使用其他归因丰富广告数据
SingularAdData 公开了用于展示级别Meta数据的 setter 方法。当您的聚合平台提供这些信息时,
使用它们以放置位置、广告单Meta、聚合分组和精度细节来丰富事件。
与 Android SDK 不同,
SingularAdData
的 setter 返回
void
,且不可链式调用。请将每个 setter 单独写在一行。
| 方法 | 描述 |
|---|---|
setNetworkName:
|
覆盖网络名称(默认为传递给构造函数的广告平台)。在聚合中, 当底层网络与聚合平台不同时使用。 |
setAdType:
|
广告形式(例如
@"Rewarded"、
@"Interstitial"、
@"Banner")。
|
setGroupType:
|
您的聚合平台定义的广告组分类。 |
setImpressionId:
|
展示的唯一标识符,用于去重和对账。 |
setAdPlacementName:
|
人类可读的放置位置名称(例如
@"level_complete")。
|
setAdUnitId:
|
平台特定的广告单Meta标识符(例如 AdMob 的
@"ca-app-pub-..."
)。
|
setAdUnitName:
|
人类可读的广告单Meta名称。 |
setAdGroupId:
|
来自聚合平台的广告组标识符。 |
setAdGroupName:
|
人类可读的广告组名称。 |
setAdGroupPriority:
|
在聚合 waterfall 中分配给广告组的优先级。 |
setPrecision:
|
平台支持时的收入精度指示符(例如
@"publisher_provided"、
@"estimated"、
@"exact")。
|
setPlacementId:
|
平台特定的放置位置标识符。 |
setLimitDataSharing:
|
为这个单一广告事件覆盖全局数据共享限制, 例如用户已选择退出个性化广告时。 |
Swift 示例:
let data = SingularAdData(adPlatform: "AdMob",
withCurrency: "USD",
withRevenue: NSNumber(value: 0.05))
data.setAdUnitId("ca-app-pub-123456789/1234567890")
data.setAdType("Rewarded")
data.setAdPlacementName("level_complete")
data.setImpressionId("imp_abc123")
data.setPrecision("publisher_provided")
Singular.adRevenue(data)
Objective-C 示例:
SingularAdData *data = [[SingularAdData alloc]
initWithAdPlatform:@"AdMob"
withCurrency:@"USD"
withRevenue:@(0.05)];
[data setAdUnitId:@"ca-app-pub-123456789/1234567890"];
[data setAdType:@"Rewarded"];
[data setAdPlacementName:@"level_complete"];
[data setImpressionId:@"imp_abc123"];
[data setPrecision:@"publisher_provided"];
[Singular adRevenue:data];
测试和验证
验证收入报告
测试您的广告收入实现,确保数据正确传输到 Singular。
- 检查日志: 验证收入回调日志显示正确的值和货币
- 测试广告: 加载并展示测试广告以触发收入事件
- 控制台验证: 确认收入在 24 小时内显示在 Singular 控制台中
- 数据精度: 验证收入金额与您的聚合平台报告相匹配
故障排除: 如果收入未显示在 Singular 中,请检查:
- 广告收入归因已在您的 Singular 账户中启用
- 收入值大于 0
- 货币代码是有效的 ISO 4217 代码
- 平台名称与 Singular 的预期格式相匹配