主题
初始化
文档版本 v1.0.0
本页对应 iOS SDK 1.0.0,与离线包 docs/ 目录里的同一份文档内容一致。其他版本见文档中心。
两步,顺序不可颠倒:setup 只读本地配置,start 才联网并按需初始化各广告平台的 SDK。
接入方只配两个凭证
接入方不需要配置穿山甲 / 优量汇的应用 ID,也不需要配平台广告位 ID。
原因是平台的运营模式 —— 每个开发者在穿山甲、优量汇后台都是独立创建的应用, 应用 ID 和广告位各不相同。如果这些值要写进宿主 App,平台每接一个开发者就得重新出一个接入包, 接入成本没有下限。所以广告平台的应用 ID 和广告位 ID 一律由配置服务下发,SDK 侧没有任何本地写入口。
| 谁来配 | 配什么 |
|---|---|
| 接入方 | AppID + App 密钥,就这两个。服务器地址内置在 SDK 里,不用配 |
| 平台运营 | 各广告平台的应用 ID、广告位与平台广告位 ID、底价、超时、瀑布顺序 —— 全部在管理后台维护,改动不需要接入方发版 |
AppID 和 App 密钥由平台运营在管理后台「应用管理 → 接入」里查看并交付。
第一步:setup
在 application:didFinishLaunchingWithOptions: 中调用。只读取本地配置, 不联网、不采集任何设备信息,一家广告平台也不会被初始化。
objc
#import <AdMixCore/AdMixCore.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[AdMix setupWithAppId:@"AppID" appKey:@"App密钥"];
return YES;
}
@end绝大多数接入方写到这里就够了。AppID 或 App 密钥为空时 setup 直接抛 NSInvalidArgumentException,开发期第一次运行就能发现。
凭证建议从构建配置注入
把 AppID 与 App 密钥写进 .xcconfig、经 Info.plist 读出,调试包与正式包可以各用一套, 也不会随源码提交进仓库。App 密钥会进安装包,它是按应用分配、可在后台作废轮换的客户端凭证; AppSecret 不得出现在 App 里,见《配置与凭证》。
可选项
需要打开详细日志、处理用户关闭个性化推荐、或按渠道 / 人群分组时,用带可选项的重载:
objc
AdMixConfig *options = [AdMixConfig new];
#if DEBUG
options.debug = YES; // 输出完整调度日志,上线包请关闭
#endif
options.limitPersonalAds = userClosedPersonalAds; // 用户是否关闭个性化推荐
options.channel = @"渠道标识"; // 流量分组,可选
options.subChannel = @"子渠道标识";
[AdMix setupWithAppId:@"AppID" appKey:@"App密钥" options:options];| 可选项 | 默认 | 为什么留给接入方 |
|---|---|---|
debug | NO | 只有接入方知道当前是不是调试包。关闭时仍会输出配置拉取、初始化结果、错误等关键日志 |
limitPersonalAds | NO | 用户关闭个性化推荐后必须透传给各广告平台,只有接入方知道用户的选择。须在首次加载广告之前确定,透传方式见《合规与隐私》「个性化广告开关」 |
channel / subChannel / userValueGroup / segmentCustomInfos | 不填 | 流量分组,只对穿山甲(GroMore 通道)生效,不填也能正常出广告 |
关于流量分组:这些值会随请求带给 GroMore,GroMore 后台可以按分组配置不同瀑布流 (例如按用户价值分层)。取值应由宿主 App 或服务端决定,不要写死。
流量分组字段不得放入个人信息
这些字段会上传给广告平台。手机号、身份证号、精确位置、可关联到自然人的账号 ID 一律不得放入, 否则构成未告知的个人信息共享。详见《合规与隐私》「流量分组标识」。
第二步:start
必须在用户同意隐私政策之后调用。completion 在主线程回调。
objc
[AdMix startWithCompletion:^(NSError *error) {
if (error) { /* 本次会话不要请求广告,按无广告降级 */ return; }
/* 可以开始请求广告 */
}];start 做的事是把配置拿到手,而不是初始化广告平台:
| 情形 | 行为 |
|---|---|
| 本地已有配置(上一次拉到的缓存) | 立即成功回调,不等网络,同时后台静默刷新一次 |
| 本地什么都没有(装机后真正的第一次启动) | 必须等一次网络请求,拿到广告平台配置才知道要初始化谁。最多等 5 秒 |
| 超时或拉取失败 | 回调 error。此时不要请求广告,按无广告降级;请求仍会在后台跑完,App 回到前台时若仍没有配置会自动重拉,[AdMix isReady] 变为 YES 后即可请求 |
error 为 nil = 可以请求广告,非 nil = 不要请求。判据只有一条:广告平台配置到手没有。
隐私授权之前不得调用 start
用户同意隐私政策前调用 start 属于违规采集个人信息,应用商店会驳回上架。
还有三条同样会被驳回的细节:同意结果必须持久化(否则每次启动都弹,用户当成弹窗广告)、 点「不同意」时 App 仍须可用(只是没广告,禁掉广告入口并留一个「重新查看隐私政策」入口)、 弹窗文案要能对应到实际集成的 SDK。详见《合规与隐私》「延迟初始化」。
SDK 侧另有一道兜底:用户未同意时任何 loadAd 都会直接失败(错误码 -1006), 不管从哪个入口进来都不会碰到任何广告 SDK。
首次安装的网络授权
国行 iPhone 首次安装后,系统会询问是否允许 App 使用无线数据。用户点击允许之前, App 的全部网络请求都会被系统拦截(NSURLErrorDomain -1009),日志会带上 「允许 App 使用无线数据」的提示。
此时首次 start 会因拿不到配置而回调失败;用户允许后 App 回到前台,SDK 自动重新拉取配置, isReady 随即变为 YES,无需重启 App。首次启动的开屏因此通常没有广告,属预期现象。
广告平台是按需初始化的
start 成功之后,仍然一家广告平台都没有初始化。 某家只在「首次加载到配置了它的广告位」时才初始化。
两个收益:
- 启动更快 —— 用不到的广告平台一次初始化都不做
- 采集更少 —— 未被使用的广告平台一行代码都不跑、不采集任何设备信息。 比如某个 App 只用激励视频,而激励视频的瀑布里没有优量汇,那优量汇 SDK 在这台设备上就从未运行过
接入方不需要为此写任何代码,SDK 内部完成。只有一处需要留意:
首次加载会多出一次广告平台初始化的耗时
开屏的超时预算本来就只有 3 秒出头,广告平台的首次初始化可能吃掉其中一部分。两个办法,任选:
- 用
+[AdMix preloadUnitId:completion:]提前预热(开屏本来就该预加载,见《预加载与缓存》), 初始化成本由预加载承担 - 让平台运营把开屏的
total_timeout_ms放宽一点,留出余量
等待初始化的时间不会超过该广告位自己的 total_timeout_ms, 不会出现「业务侧早就放行进首页了,广告还在等初始化」的情况。
跟踪授权
SDK 不弹出跟踪授权(App Tracking Transparency)弹窗,弹窗的文案与时机由宿主 App 决定。 SDK 只读取授权状态并随配置请求上报,用于区分填充率波动的原因。
宿主 App 的 Info.plist 需要包含 NSUserTrackingUsageDescription。建议在 start 成功、 首页展示之后延迟数秒再请求授权,避免冷启动时立即弹窗:
objc
#import <AppTrackingTransparency/AppTrackingTransparency.h>
if (@available(iOS 14, *)) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {}];
});
}- 未授权时两家广告平台照常展示广告,填充率与单价通常低于已授权的用户
- 优量汇 SDK 在隐私清单中声明了跟踪域名,未授权时系统会直接拦截发往该域名的请求, 这一行为由系统控制,不影响 App 运行
- 授权弹窗与隐私政策弹窗是两件事:前者不能代替后者,
start的时机只看隐私政策的同意结果
Swift 写法
SDK 是 Objective-C API,Swift 工程 import AdMixCore 后直接调用,不需要桥接头文件。 方法名按 Swift 习惯做了重命名:
swift
import AdMixCore
@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// 只读本地配置:不联网、不采集、不碰任何广告 SDK
let options = AdMixConfig()
#if DEBUG
options.debug = true
#endif
AdMix.setup(appId: "AppID", appKey: "App密钥", options: options)
return true
}
}
// 用户点「同意」之后调用,不是在 didFinishLaunching 里
func onPrivacyAgreed() {
AdMix.start { error in
if error != nil { return } // 按无广告降级,本次会话不要请求
// 可以请求广告
}
}| Objective-C | Swift |
|---|---|
+[AdMix setupWithAppId:appKey:] | AdMix.setup(appId:appKey:) |
+[AdMix setupWithAppId:appKey:options:] | AdMix.setup(appId:appKey:options:) |
+[AdMix startWithCompletion:] | AdMix.start(completion:) |
+[AdMix preloadUnitId:completion:] | AdMix.preload(unitId:completion:) |
+[AdMix cachedCountForUnitId:] | AdMix.cachedCount(unitId:) |
-[AdMixRewardAd loadAd] 等三种广告位的 loadAd | load() |
-[AdMixRewardAd showFromViewController:] | show(from:) |
-[AdMixSplashAd showInWindow:bottomView:] | show(in:bottomView:) |
-[AdMixBannerAd loadInContainer:viewController:] | load(inContainer:viewController:) |
三种广告位的 Swift 写法见《开屏广告》《激励视频广告》《Banner 广告》各自的末节。
日志排查
SDK 日志走系统统一日志,subsystem 为 com.admix.sdk、category 为 AdMix。 Xcode 控制台过滤 AdMix 即可;不连 Xcode 时用「控制台」App 选中设备、搜索 subsystem:com.admix.sdk。 debug = YES 时输出完整调度过程(带 [D] 前缀的行)。
启动阶段先核对这几行(不受 debug 开关影响,关了也打):
text
适配器自检: 发现 [csj,gdt] ← 实际链接进 App 的广告平台;一个都没有见《引入依赖》「链接参数 -ObjC」
拉取远端配置 … adapters=[csj,gdt] … ← 上报给平台的能力清单,服务端据此裁剪下发
适配器就绪: csj (ADN SDK 7.8.0.8) ← 首次用到该广告平台时打;带 [W] 的是广告 SDK 版本与锁定版本不一致调度阶段的典型输出:
text
开始加载 reward_main 竞价位=1 瀑布层=1
竞价返回 gdt ecpm=***
竞价最高价: gdt ***
[W] 瀑布层无填充 csj …,下探
最终胜出: gdt ecpm=***日志里的价格和平台广告位 ID 一律显示为 ***:设备日志对宿主开发者可见,这两项不对接入方开放, 这与 debug 开关无关。需要定位到具体广告位时,把日志里的广告位标识、广告平台和时间点 交给平台运营查询。
上线前请将 debug 置为 NO。