iOS SDK
The ADBNK iOS SDK serves ads in native iOS apps. Its API is modeled on Google Mobile Ads (AdMob): you'll find AdRequest, FullScreenContentCallback, a reward listener and the other familiar pieces.
Version: the version of the SDK package you received.
Requirements
| Item | Requirement |
|---|---|
| iOS | 12.0+ |
| Swift | 5.0+ |
| Frameworks | Linked automatically. ATT and StoreKit are weak-linked, so the SDK still runs on iOS 12. |
Each placement also needs a zone ID. To get one, create an App SDK zone in the publisher console, open Zones, and click Get code.
Installation
The SDK is delivered as a signed binary AdbnkSDK.xcframework. To get the SDK package, contact your ADBNK account manager.
- Drag
AdbnkSDK.xcframeworkinto your Xcode project and add it to your app target. - In the target's General → Frameworks, Libraries, and Embedded Content, set it to Embed & Sign.
- Make sure the target's minimum deployment version is iOS 12.0 or later.
The system frameworks the SDK uses are linked automatically.
The SDK's PrivacyInfo.xcprivacy privacy manifest is embedded in the xcframework, and Xcode merges it into your app's privacy report automatically.
Initialization
Initialize the SDK once at launch, before you load any ad:
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
Loading an ad before initialize is a programming error and stops the app at a precondition. Always initialize first.
AdbnkConfig fields (all optional):
| Field | Default | Description |
|---|---|---|
testMode | false | See Test mode |
gdprConsent | nil | GDPR consent. nil means unknown and is treated as not consented. See Privacy |
coppaCompliance | false | Treats every request as child-directed |
AdbnkSDK.shared also exposes getAppId(), getConfig(), updateConfig(_:), setGDPRConsent(_:), setCOPPACompliance(_:), setTestMode(_:) and setIdfaSyncEnabled(_:).
AdRequest
Every load method takes an optional AdRequest. Each field is optional too.
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() returns a request with every field left at its default.
Ad formats
| Format | Class | Style |
|---|---|---|
| Banner | BannerAdView | A UIView in your layout |
| Float banner (pinned to the top or bottom) | FloatBannerAd | Overlay window |
| Native | NativeAd | You render the assets yourself |
| Interstitial | InterstitialAd | Full screen |
| Rewarded | RewardedAd | Full screen, with reward |
| Rewarded interstitial | RewardedInterstitialAd | Full screen, with reward |
| Splash | SplashAd | Full screen at launch |
| App open | AppOpenAd | Full screen when the app returns to the foreground |
| Popup | PopupAd | Dialog-style overlay |
Use a zone whose format matches the class you load.
Full-screen formats: shared pattern
Interstitial, rewarded, rewarded interstitial, splash, app open and popup ads all work the same way:
- Call
XxxAd.load(zoneId:request:completion:).requestdefaults tonil. - Attach a
FullScreenContentCallbackwithsetFullScreenContentCallback(_:). - Call
show(from: viewController).
Each ad object can be shown once, and isReady() turns false after it is shown or once it expires. Load a new ad for every impression.
public protocol FullScreenContentCallback: AnyObject {
func onAdShowedFullScreenContent()
func onAdDismissedFullScreenContent()
func onAdFailedToShowFullScreenContent(_ error: AdError)
func onAdImpression()
func onAdClicked()
} // every method has a default empty implementationKeep strong references
The SDK holds fullScreenContentCallback and adListener weakly. Keep both the ad object and its callback or listener object alive, for example as properties of your view controller, until the ad is dismissed.
Interstitial
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
}
}There is also an AdMob-style generic overload: load(zoneId:adRequest:callback:). It takes any object that conforms to AdLoadCallback with Ad set to the matching ad class.
Rewarded and rewarded interstitial
RewardedAd and RewardedInterstitialAd share the same API. Grant the reward inside the reward listener. The SDK calls it at most once per ad.
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)
}You set the reward amount and type on the zone in the publisher console. They reach the listener as RewardItem(amount: Int, type: String).
Splash
Load the splash ad early in launch. Show it once it loads, then move on to your main UI when it is dismissed or fails.
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)
}The splash ad shows a countdown with a skip button.
App open
You can load and show it manually, the same way as an interstitial:
AppOpenAd.load(zoneId: "YOUR_ZONE_ID") { ad, _ in self.appOpenAd = ad }
// later:
if let ad = appOpenAd, ad.isReady() { ad.show(from: rootViewController) }Or let the SDK handle it. Call setup once after initialize, and the SDK preloads an ad and shows it each time the app becomes active:
AppOpenAd.setup(zoneId: "YOUR_ZONE_ID")Popup
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)
}Banner
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 values: .banner (320×50), .largeBanner (320×100), .mediumRectangle (300×250), .fullBanner (468×60), .leaderboard (728×90) and .adaptive. Pick the one that matches your zone, and give the view a frame or constraints of that size.
Auto-refresh uses the interval set on the zone in the console. If the zone has none, the refreshInterval property applies (in seconds; 0 turns refresh off).
Float banner
FloatBannerAd floats in its own window at the top or bottom of the screen, so it takes up no layout space. It shows itself as soon as it loads.
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()Keep a strong reference to floatBanner for as long as it should stay on screen.
Native
The SDK returns the native ad's assets and you lay them out in your own views:
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() {}
}| Method | Content |
|---|---|
getHeadline() | Title |
getBody() | Description |
getCallToAction() | Button text |
getAdvertiser() | Advertiser or sponsor |
getIconUrl() / loadIcon(completion:) | Icon |
getMainImageUrl() / loadMainImage(completion:) | Main image |
getRating() | Rating from 1 to 5 (optional) |
getPrice() | Price (optional) |
getSponsoredLabel() | Ad label text |
getAssets() | All assets as NativeAdAssets |
If clickableViews is empty, the whole container is clickable. If you can't use registerViewForInteraction, call recordClick() only from a real user tap on the ad.
Click handling
Clicks need no code from you. The SDK records the click, then opens the ad's app deep link or universal link if there is one. If that can't be opened, it opens the landing page.
Privacy and compliance
The SDK never shows a consent prompt or the ATT dialog itself. Your app decides when to ask.
App Tracking Transparency (ATT)
- To use the IDFA for personalized ads and attribution, request authorization yourself at a moment that makes sense in your app, and add
NSUserTrackingUsageDescriptiontoInfo.plist. - If the user doesn't grant authorization, the SDK neither reads nor sends the IDFA. Ads still serve normally.
- To keep the SDK from using the IDFA even after the user authorizes it, call
AdbnkSDK.shared.setIdfaSyncEnabled(false).
import AppTrackingTransparency
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { _ in }
}GDPR (EEA and UK users). Until you set it, consent is unknown and the SDK treats it as not consented. Collect consent with your CMP, then pass the result (true or false) to the SDK before you load ads:
AdbnkSDK.shared.setGDPRConsent(userConsented)Non-personalized ads. Use AdRequest.Builder().setNonPersonalizedAds(true).
COPPA and child-directed apps:
AdbnkSDK.shared.setCOPPACompliance(true) // whole app
AdRequest.Builder().setTagForChildDirected(true).build() // per request
AdRequest.Builder().setTagForUnderAgeOfConsent(true).build() // under age of consentWhen consent is withheld, or for child-directed or non-personalized requests, the SDK does not use the IDFA or other advertising identifiers. Limited technical data is still processed to keep traffic safe.
App Store privacy labels. The SDK ships its own privacy manifest. Use it, together with your own data use, to fill in your App Privacy answers.
Test mode
testMode/setTestMode(true)marks requests as test traffic. ADBNK doesn't yet serve dedicated test creatives, so the ads you see during development are live. Don't click ads over and over on your own devices.- The SDK handles traffic-quality protection automatically. There is nothing to configure.
Error codes
AdError conforms to Swift Error and has code and message.
| Code | Meaning |
|---|---|
| 1001 | Zone ID is empty or invalid |
| 1002 | No fill: no ad available right now. Retry later, and don't retry in a tight loop |
| 1003 | Network error |
| 1004 | Internal error |
| 1005 | Ad expired or isn't ready. Load a new one |
| 1006 | This ad object has already been shown |
| 1007 | A load is already in progress |
| 1008 | This SDK version doesn't support the zone's ad format |
FAQ
Every request returns 1002. Check three things: you copied the zone ID from Get code, the zone is an App SDK zone whose format matches the class you're loading, and the zone is active.
Callbacks never fire. The callback or listener object has most likely been deallocated, because the SDK only holds it weakly. Store it in a property.
Can I keep using the listener: parameter of show(from:listener:)? Yes. The legacy FullScreenAdListener and RewardedAdListener protocols still work but are deprecated. Prefer FullScreenContentCallback and setOnUserEarnedRewardListener.
What should I send support? Every full-screen ad has getResponseInfo().responseId. Include it when you contact support.