iOS SDK
ADBNK iOS SDK 用于在原生 iOS App 中展示广告。API 参照 Google Mobile Ads(AdMob)设计:AdRequest、FullScreenContentCallback、奖励回调等用法都与之相同。
版本:以你拿到的 SDK 包为准。
环境要求
| 项目 | 要求 |
|---|---|
| iOS | 12.0+ |
| Swift | 5.0+ |
| 系统框架 | 自动链接。ATT 与 StoreKit 为弱链接,SDK 在 iOS 12 上照常运行 |
每个广告位还需要一个广告位 ID。在流量主后台创建 App SDK 广告位,进入广告位管理,点击获取代码即可获取。
安装
SDK 以签名的二进制 AdbnkSDK.xcframework 形式交付。如需获取 SDK 包,请联系你的 ADBNK 客户经理。
- 把
AdbnkSDK.xcframework拖入 Xcode 工程,并添加到 App target。 - 在 target 的 General → Frameworks, Libraries, and Embedded Content 中将其设为 Embed & Sign。
- 确认 target 的最低部署版本为 iOS 12.0 或更高。
SDK 用到的系统框架会自动链接。
SDK 的隐私清单 PrivacyInfo.xcprivacy 已内嵌在 xcframework 中,Xcode 会自动把它合并进你 App 的隐私报告。
初始化
在启动时初始化一次,且须在加载任何广告之前完成:
import AdbnkSDK
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AdbnkSDK.shared.initialize(
appId: Bundle.main.bundleIdentifier ?? "your.app.id" // your app identifier
)
return true
}WARNING
在 initialize 之前加载广告属于编程错误,会触发 precondition 使 App 终止。请务必先初始化。
AdbnkConfig 字段(均可选):
| 字段 | 默认值 | 说明 |
|---|---|---|
testMode | false | 见测试模式 |
gdprConsent | nil | GDPR 同意状态。nil 表示未知,按未同意处理,见隐私与合规 |
coppaCompliance | false | 将所有请求视为面向儿童 |
AdbnkSDK.shared 还提供 getAppId()、getConfig()、updateConfig(_:)、setGDPRConsent(_:)、setCOPPACompliance(_:)、setTestMode(_:) 和 setIdfaSyncEnabled(_:)。
AdRequest
每个加载方法都接受一个可选的 AdRequest,其中每个字段也都是可选的。
let request = AdRequest.Builder()
.addKeyword("sports")
.setContentUrl("https://example.com/article/123")
.setNonPersonalizedAds(false)
.setTagForChildDirected(nil) // true / false / nil = unspecified
.setTagForUnderAgeOfConsent(nil)
.putExtra("placement", "home_feed")
.build()AdRequest.default() 返回所有字段均为默认值的请求。
广告格式
| 格式 | 类 | 形式 |
|---|---|---|
| 横幅 Banner | BannerAdView | 放在布局中的 UIView |
| 悬浮横幅(固定在顶部或底部) | FloatBannerAd | 独立浮层窗口 |
| 原生 Native | NativeAd | 由你自行渲染素材 |
| 插屏 Interstitial | InterstitialAd | 全屏 |
| 激励 Rewarded | RewardedAd | 全屏,带奖励 |
| 激励插屏 Rewarded interstitial | RewardedInterstitialAd | 全屏,带奖励 |
| 开屏 Splash | SplashAd | 启动时全屏 |
| 应用打开 App open | AppOpenAd | App 回到前台时全屏 |
| 弹窗 Popup | PopupAd | 对话框式浮层 |
请使用格式与所加载类相匹配的广告位。
全屏格式:通用流程
插屏、激励、激励插屏、开屏、应用打开和弹窗广告的用法相同:
- 调用
XxxAd.load(zoneId:request:completion:),request默认为nil。 - 用
setFullScreenContentCallback(_:)设置FullScreenContentCallback。 - 调用
show(from: viewController)。
每个广告对象只能展示一次;展示后或过期后 isReady() 变为 false。每次展示都请加载新的广告。
public protocol FullScreenContentCallback: AnyObject {
func onAdShowedFullScreenContent()
func onAdDismissedFullScreenContent()
func onAdFailedToShowFullScreenContent(_ error: AdError)
func onAdImpression()
func onAdClicked()
} // every method has a default empty implementation保持强引用
SDK 对 fullScreenContentCallback 和 adListener 是弱引用。请在广告关闭前一直持有广告对象及其回调/监听对象,例如作为 view controller 的属性。
插屏
import AdbnkSDK
final class GameViewController: UIViewController, FullScreenContentCallback {
private var interstitial: InterstitialAd?
func loadInterstitial() {
InterstitialAd.load(zoneId: "YOUR_ZONE_ID") { [weak self] ad, error in
guard let self = self, let ad = ad else { return }
ad.setFullScreenContentCallback(self)
self.interstitial = ad
}
}
func showInterstitial() {
guard let ad = interstitial, ad.isReady() else { return }
ad.show(from: self)
}
func onAdDismissedFullScreenContent() {
interstitial = nil
loadInterstitial() // preload the next one
}
func onAdFailedToShowFullScreenContent(_ error: AdError) {
interstitial = nil
}
}另有 AdMob 风格的泛型重载 load(zoneId:adRequest:callback:),接受任何遵循 AdLoadCallback 且 Ad 为对应广告类的对象。
激励与激励插屏
RewardedAd 与 RewardedInterstitialAd 的 API 相同。请在奖励回调中发放奖励,SDK 对每个广告最多调用一次。
private var rewardedAd: RewardedAd?
func loadRewarded() {
RewardedAd.load(zoneId: "YOUR_ZONE_ID") { [weak self] ad, error in
guard let self = self, let ad = ad else { return }
ad.setOnUserEarnedRewardListener { reward in
self.grantReward(amount: reward.amount, type: reward.type)
}
ad.setFullScreenContentCallback(self)
self.rewardedAd = ad
}
}
func showRewarded() {
guard let ad = rewardedAd, ad.isReady() else { return }
ad.show(from: self)
}奖励数量和类型在流量主后台的广告位上设置,以 RewardItem(amount: Int, type: String) 的形式传给回调。
开屏
在启动阶段尽早加载开屏广告,加载成功后展示;广告关闭或展示失败后进入主界面。
SplashAd.load(zoneId: "YOUR_ZONE_ID") { [weak self] ad, error in
guard let self = self else { return }
guard let ad = ad else { self.goToMain(); return }
self.splashAd = ad
ad.setFullScreenContentCallback(self) // call goToMain() in onAdDismissed / onAdFailedToShow
ad.show(from: self)
}开屏广告带倒计时和跳过按钮。
应用打开
可以像插屏一样手动加载和展示:
AppOpenAd.load(zoneId: "YOUR_ZONE_ID") { ad, _ in self.appOpenAd = ad }
// later:
if let ad = appOpenAd, ad.isReady() { ad.show(from: rootViewController) }也可以交给 SDK 处理:在 initialize 之后调用一次 setup,SDK 会预加载广告,并在 App 每次变为活跃时展示:
AppOpenAd.setup(zoneId: "YOUR_ZONE_ID")弹窗
PopupAd.load(zoneId: "YOUR_ZONE_ID") { [weak self] ad, _ in
guard let self = self, let ad = ad else { return }
self.popupAd = ad
ad.show(from: self)
}横幅
final class FeedViewController: UIViewController, BannerAdListener {
private let banner = BannerAdView(frame: CGRect(x: 0, y: 0, width: 320, height: 50))
override func viewDidLoad() {
super.viewDidLoad()
banner.zoneId = "YOUR_ZONE_ID"
banner.bannerSize = .banner // 320x50
banner.adListener = self // weak reference
view.addSubview(banner)
banner.loadAd()
}
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated); banner.resume() }
override func viewWillDisappear(_ animated: Bool) { banner.pause(); super.viewWillDisappear(animated) }
deinit { banner.destroy() }
func onAdLoaded() {}
func onAdFailedToLoad(_ error: AdError) {}
func onAdImpression() {}
func onAdClicked() {}
}BannerSize 取值:.banner(320×50)、.largeBanner(320×100)、.mediumRectangle(300×250)、.fullBanner(468×60)、.leaderboard(728×90)和 .adaptive。请选择与广告位一致的尺寸,并给视图设置相同尺寸的 frame 或约束。
自动刷新使用后台广告位上设置的间隔;广告位未设置时,使用 refreshInterval 属性(单位秒,0 表示不刷新)。
悬浮横幅
FloatBannerAd 在独立窗口中悬浮于屏幕顶部或底部,不占用布局空间。它加载成功后会自动展示。
let floatBanner = FloatBannerAd()
floatBanner.zoneId = "YOUR_ZONE_ID"
floatBanner.position = .bottom
floatBanner.showCloseButton = true
floatBanner.margin = 0
floatBanner.autoHideAfter = 0 // seconds, 0 = never
floatBanner.adListener = self // weak reference
floatBanner.loadAd()
// floatBanner.hide() / show() / isCurrentlyShowing() / isCurrentlyLoaded()
// floatBanner.pause() / resume() / destroy()需要它保持在屏幕上期间,请一直持有 floatBanner 的强引用。
原生
SDK 返回原生广告的素材,由你用自己的视图排版:
final class FeedCell: UITableViewCell, NativeAdListener {
private var nativeAd: NativeAd?
func loadAd() {
NativeAd.load(zoneId: "YOUR_ZONE_ID") { [weak self] ad, error in
guard let self = self, let ad = ad else { return }
self.nativeAd = ad
self.titleLabel.text = ad.getHeadline()
self.bodyLabel.text = ad.getBody()
self.ctaButton.setTitle(ad.getCallToAction(), for: .normal)
self.sponsoredLabel.text = ad.getSponsoredLabel() // always show an ad label
ad.loadIcon { image in self.iconView.image = image }
ad.loadMainImage { image in self.mainImageView.image = image }
ad.setListener(self)
// Required: lets the SDK measure impressions and handle clicks
ad.registerViewForInteraction(container: self.contentView,
clickableViews: [self.ctaButton, self.mainImageView])
}
}
override func prepareForReuse() {
super.prepareForReuse()
nativeAd?.destroy()
nativeAd = nil
}
func onAdClicked() {}
func onAdImpression() {}
}| 方法 | 内容 |
|---|---|
getHeadline() | 标题 |
getBody() | 描述 |
getCallToAction() | 按钮文案 |
getAdvertiser() | 广告主或赞助方 |
getIconUrl() / loadIcon(completion:) | 图标 |
getMainImageUrl() / loadMainImage(completion:) | 主图 |
getRating() | 评分,1 到 5(可选) |
getPrice() | 价格(可选) |
getSponsoredLabel() | 广告标识文案 |
getAssets() | 以 NativeAdAssets 返回全部素材 |
clickableViews 为空时,整个容器都可点击。若界面无法使用 registerViewForInteraction,请仅在用户真实点击广告时调用 recordClick()。
点击处理
点击无需你编写代码。SDK 记录点击后,若广告带有 App deeplink 或通用链接则打开它;无法打开时,打开落地页。
隐私与合规
SDK 自身从不弹出同意框或 ATT 授权框,何时询问由你的 App 决定。
App Tracking Transparency(ATT)
- 如需使用 IDFA 进行个性化广告和归因,请在 App 中合适的时机自行请求授权,并在
Info.plist中添加NSUserTrackingUsageDescription。 - 用户未授权时,SDK 既不读取也不发送 IDFA。广告照常展示。
- 如果希望用户授权后 SDK 也不使用 IDFA,调用
AdbnkSDK.shared.setIdfaSyncEnabled(false)。
import AppTrackingTransparency
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { _ in }
}GDPR(欧洲经济区及英国用户)。 未设置时同意状态为未知,SDK 按未同意处理。用你的 CMP 收集同意,并在加载广告之前把结果(true 或 false)传给 SDK:
AdbnkSDK.shared.setGDPRConsent(userConsented)非个性化广告。 使用 AdRequest.Builder().setNonPersonalizedAds(true)。
COPPA 与面向儿童的 App:
AdbnkSDK.shared.setCOPPACompliance(true) // whole app
AdRequest.Builder().setTagForChildDirected(true).build() // per request
AdRequest.Builder().setTagForUnderAgeOfConsent(true).build() // under age of consent未获同意、或面向儿童/非个性化的请求,SDK 不使用 IDFA 等广告标识。为保障流量安全,仍会处理有限的技术数据。
App Store 隐私标签。 SDK 自带隐私清单。请结合它与你自身的数据使用情况填写 App 隐私问卷。
测试模式
testMode/setTestMode(true)会把请求标记为测试流量。ADBNK 目前不提供专用测试素材,开发期间看到的是真实广告。请勿在自己的设备上反复点击广告。- SDK 自动处理流量质量保护,无需任何配置。
错误码
AdError 遵循 Swift Error,包含 code 和 message。
| 错误码 | 含义 |
|---|---|
| 1001 | 广告位 ID 为空或无效 |
| 1002 | 无填充:暂无可用广告。请稍后重试,不要密集重试 |
| 1003 | 网络错误 |
| 1004 | 内部错误 |
| 1005 | 广告已过期或未就绪,请重新加载 |
| 1006 | 该广告对象已展示过 |
| 1007 | 已有加载在进行中 |
| 1008 | 当前 SDK 版本不支持该广告位的广告格式 |
常见问题
每次请求都返回 1002。 检查三点:广告位 ID 是从获取代码中复制的;广告位是 App SDK 广告位,且格式与你加载的类一致;广告位处于启用状态。
回调从不触发。 多半是回调或监听对象已被释放,因为 SDK 只弱引用它。请把它保存在属性中。
还能继续使用 show(from:listener:) 的 listener: 参数吗? 可以。旧的 FullScreenAdListener 和 RewardedAdListener 协议仍可使用,但已废弃。建议改用 FullScreenContentCallback 和 setOnUserEarnedRewardListener。
联系支持时需要提供什么? 每个全屏广告都有 getResponseInfo().responseId,联系支持时请附上。