Skip to content

iOS SDK ​

ADBNK iOS SDK 用于在原生 iOS App 中展示广告。API 参照 Google Mobile Ads(AdMob)设计:AdRequest、FullScreenContentCallback、奖励回调等用法都与之相同。

版本:以你拿到的 SDK 包为准。

环境要求 ​

项目要求
iOS12.0+
Swift5.0+
系统框架自动链接。ATT 与 StoreKit 为弱链接,SDK 在 iOS 12 上照常运行

每个广告位还需要一个广告位 ID。在流量主后台创建 App SDK 广告位,进入广告位管理,点击获取代码即可获取。

安装 ​

SDK 以签名的二进制 AdbnkSDK.xcframework 形式交付。如需获取 SDK 包,请联系你的 ADBNK 客户经理。

  1. 把 AdbnkSDK.xcframework 拖入 Xcode 工程,并添加到 App target。
  2. 在 target 的 General → Frameworks, Libraries, and Embedded Content 中将其设为 Embed & Sign。
  3. 确认 target 的最低部署版本为 iOS 12.0 或更高。

SDK 用到的系统框架会自动链接。

SDK 的隐私清单 PrivacyInfo.xcprivacy 已内嵌在 xcframework 中,Xcode 会自动把它合并进你 App 的隐私报告。

初始化 ​

在启动时初始化一次,且须在加载任何广告之前完成:

swift
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 字段(均可选):

字段默认值说明
testModefalse见测试模式
gdprConsentnilGDPR 同意状态。nil 表示未知,按未同意处理,见隐私与合规
coppaCompliancefalse将所有请求视为面向儿童

AdbnkSDK.shared 还提供 getAppId()、getConfig()、updateConfig(_:)、setGDPRConsent(_:)、setCOPPACompliance(_:)、setTestMode(_:) 和 setIdfaSyncEnabled(_:)。

AdRequest ​

每个加载方法都接受一个可选的 AdRequest,其中每个字段也都是可选的。

swift
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() 返回所有字段均为默认值的请求。

广告格式 ​

格式类形式
横幅 BannerBannerAdView放在布局中的 UIView
悬浮横幅(固定在顶部或底部)FloatBannerAd独立浮层窗口
原生 NativeNativeAd由你自行渲染素材
插屏 InterstitialInterstitialAd全屏
激励 RewardedRewardedAd全屏,带奖励
激励插屏 Rewarded interstitialRewardedInterstitialAd全屏,带奖励
开屏 SplashSplashAd启动时全屏
应用打开 App openAppOpenAdApp 回到前台时全屏
弹窗 PopupPopupAd对话框式浮层

请使用格式与所加载类相匹配的广告位。

全屏格式:通用流程 ​

插屏、激励、激励插屏、开屏、应用打开和弹窗广告的用法相同:

  1. 调用 XxxAd.load(zoneId:request:completion:),request 默认为 nil。
  2. 用 setFullScreenContentCallback(_:) 设置 FullScreenContentCallback。
  3. 调用 show(from: viewController)。

每个广告对象只能展示一次;展示后或过期后 isReady() 变为 false。每次展示都请加载新的广告。

swift
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 的属性。

插屏 ​

swift
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 对每个广告最多调用一次。

swift
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) 的形式传给回调。

开屏 ​

在启动阶段尽早加载开屏广告,加载成功后展示;广告关闭或展示失败后进入主界面。

swift
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)
}

开屏广告带倒计时和跳过按钮。

应用打开 ​

可以像插屏一样手动加载和展示:

swift
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 每次变为活跃时展示:

swift
AppOpenAd.setup(zoneId: "YOUR_ZONE_ID")
swift
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)
}
swift
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 在独立窗口中悬浮于屏幕顶部或底部,不占用布局空间。它加载成功后会自动展示。

swift
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 返回原生广告的素材,由你用自己的视图排版:

swift
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)。
swift
import AppTrackingTransparency

if #available(iOS 14, *) {
    ATTrackingManager.requestTrackingAuthorization { _ in }
}

GDPR(欧洲经济区及英国用户)。 未设置时同意状态为未知,SDK 按未同意处理。用你的 CMP 收集同意,并在加载广告之前把结果(true 或 false)传给 SDK:

swift
AdbnkSDK.shared.setGDPRConsent(userConsented)

非个性化广告。 使用 AdRequest.Builder().setNonPersonalizedAds(true)。

COPPA 与面向儿童的 App:

swift
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,联系支持时请附上。

按 MIT 许可发布的文档内容。