iOS

The Ezoic Ads SDK for iOS lets you display banner and rewarded ads in native iOS apps. The SDK initializes Ezoic configuration, Prebid header bidding, Google Ad Manager, and consent handling from one integration point.

Requirements 🔗

  • iOS 14.0 or higher
  • Xcode 15.0 or higher
  • Swift 5.9 or higher
  • Google Mobile Ads application ID

Installation 🔗

Add the SDK with Swift Package Manager.

In Xcode, go to File > Add Package Dependencies and enter:

https://github.com/ezoic/ezoic-swift-sdk-dist.git

Select Exact Version and choose 1.1.0 (or the release version provided by Ezoic). Pinning an exact version is recommended so SDK updates are always intentional.

Or add the package to Package.swift using the release version provided for your app:

dependencies: [
    .package(url: "https://github.com/ezoic/ezoic-swift-sdk-dist.git", exact: "1.1.0")
]

The SDK is distributed as a pre-built EzoicAdsSDK.xcframework attached to each GitHub release. When you add the package, Swift Package Manager also resolves the Prebid Mobile and Google Mobile Ads SDKs as transitive dependencies — you do not need to add them yourself.

Initialize the SDK 🔗

Initialize the SDK in your AppDelegate or app startup flow before loading ads.

import EzoicAdsSDK

let config = EzoicConfiguration(
    domain: "example.com",
    debugEnabled: false,
    testMode: false
)

EzoicAds.shared.initialize(with: config) { result in
    switch result {
    case .success:
        print("Ezoic SDK initialized")
    case .failure(let error):
        print("Ezoic initialization failed: \(error)")
    }
}

domain must match the domain configured for your site in Ezoic. Authentication is handled by the app's bundle identifier and the configured domain — there is no client-side API key.

For apps using async/await on iOS 15 or higher:

try await EzoicAds.shared.initialize(with: config)

Add a Banner Ad 🔗

Create an EzoicBannerView, add it to your view hierarchy, and call loadAd() after the SDK is initialized.

import EzoicAdsSDK
import UIKit

class ViewController: UIViewController {
    private var bannerView: EzoicBannerView?

    override func viewDidLoad() {
        super.viewDidLoad()

        let adView = EzoicBannerView(adUnitIdentifier: 12345)
        adView.delegate = self
        adView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(adView)

        NSLayoutConstraint.activate([
            adView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            adView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            adView.widthAnchor.constraint(equalToConstant: EzoicBannerSize.banner.width),
            adView.heightAnchor.constraint(equalToConstant: EzoicBannerSize.banner.height)
        ])

        bannerView = adView
        adView.loadAd()
    }
}

extension ViewController: EzoicBannerViewDelegate {
    func bannerViewDidLoad(_ bannerView: EzoicBannerView) {
        print("Banner loaded")
    }

    func bannerView(_ bannerView: EzoicBannerView, didFailToLoadWithError error: EzoicError) {
        print("Banner failed: \(error)")
    }
}

Replace 12345 with your numeric Ezoic ad unit identifier. The SDK fetches the Google Ad Manager ad unit, Prebid configuration, supported sizes, targeting values, and refresh interval from Ezoic servers.

You can load ads with an adaptive default size, a typed size, one size string, or a list of size strings.

adView.loadAd()
adView.loadAd(size: EzoicBannerSize.mediumRectangle)
adView.loadAd(size: "300x250")
adView.loadAd(sizes: ["300x250", "320x50"])

Common sizes include:

  • EzoicBannerSize.banner: 320x50
  • EzoicBannerSize.largeBanner: 320x100
  • EzoicBannerSize.mediumRectangle: 300x250
  • EzoicBannerSize.fullBanner: 468x60
  • EzoicBannerSize.leaderboard: 728x90
  • EzoicBannerSize.custom(width:height:): custom dimensions

You can also use the adaptive size for the current screen width:

let adaptiveSize = EzoicBannerSize.adaptiveSize

Add a Rewarded Ad 🔗

Rewarded ads are full-screen ads that grant an in-app reward when the user finishes watching. They use a load-then-show lifecycle: load the ad ahead of time (for example, at the start of a level), then present it at a natural break and grant the reward when it is earned.

import EzoicAdsSDK

var rewardedAd: EzoicRewardedAd?

func loadRewardedAd() {
    EzoicRewardedAd.load(adUnitIdentifier: 12345) { [weak self] result in
        switch result {
        case .success(let ad):
            self?.rewardedAd = ad
            ad.delegate = self
        case .failure(let error):
            print("Rewarded load failed: \(error.localizedDescription)")
        }
    }
}

func showRewardedAd() {
    rewardedAd?.show(from: self) { reward in
        print("Earned \(reward.amount) \(reward.type)")
        grantReward(reward.amount)
    }
}

async/await is also supported on iOS 15 or higher:

rewardedAd = try await EzoicRewardedAd.load(adUnitIdentifier: 12345)

Replace 12345 with your numeric Ezoic ad unit identifier. Call show(from:) only after the ad has loaded; showing before the load completes (or after the ad was already shown) reports EzoicError.adNotReady. If you pass nil (or omit from:), the SDK presents from the app's top view controller. Rewarded ads are single-use — load a new ad for each opportunity.

Lifecycle events are delivered through EzoicRewardedAdDelegate. The reward type and amount come from the reward configured on the Google Ad Manager rewarded ad unit.

extension ViewController: EzoicRewardedAdDelegate {
    func rewardedAd(_ rewardedAd: EzoicRewardedAd, userDidEarn reward: EzoicReward) {
        grantReward(reward.amount)
    }

    func rewardedAdDidDismiss(_ rewardedAd: EzoicRewardedAd) {
        print("Rewarded ad closed")
    }
}

The delegate also reports rewardedAdDidPresent, rewardedAd(_:didFailToPresentWithError:), rewardedAdDidRecordImpression, and rewardedAdDidRecordClick. All delegate methods are optional.

Required App Setup for Ads to Serve 🔗

A few configuration steps live in your app project and on your website, not in the SDK. Apple and Google require them, and they directly affect whether — and how well — ads fill. The SDK cannot add them for you (see What the SDK does vs. what you must add), so complete all of the following before testing fill.

1. Google Mobile Ads application ID 🔗

Add your Google Mobile Ads application ID to Info.plist. This ID is provided by Ezoic and can be found in your Ezoic dashboard. Use the value assigned to your app unless your Ezoic representative gives you a different one. Also set GADIsAdManagerApp so Google Mobile Ads runs in Ad Manager mode.

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX</string>
<key>GADIsAdManagerApp</key>
<true/>

Without a valid GADApplicationIdentifier, the Google Mobile Ads SDK will not initialize and no ads will serve.

2. App Tracking Transparency (ATT) 🔗

ATT has the single biggest impact on fill and CPM. Without it, the IDFA is unavailable (all zeros) and a large share of programmatic demand will not bid or will bid much lower.

Add the tracking usage description to Info.plist. Apple requires this string to live in the app's Info.plist — it cannot be supplied by the SDK because it is shown in the App Store privacy label and the system prompt.

<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>

The SDK presents the ATT prompt for you. During initialize, when the status is still undetermined, the SDK shows the prompt and waits for the user's decision before starting the ad stack, so the IDFA (if granted) is attached to the first ad request. You only need to supply the NSUserTrackingUsageDescription string above — no extra code.

// The SDK requests ATT (if undetermined) before loading ads.
EzoicAds.shared.initialize(with: config) { _ in
    // SDK ready — banners can load with the IDFA available.
}

If you prefer to drive the ATT flow yourself (for example, to show a pre-prompt or to control its timing), set requestATTBeforeAds: false on EzoicConfiguration and request authorization before initializing the SDK so the IDFA is available on the first ad request:

import AppTrackingTransparency

let config = EzoicConfiguration(domain: "example.com", requestATTBeforeAds: false)
ATTrackingManager.requestTrackingAuthorization { _ in
    EzoicAds.shared.initialize(with: config) { _ in }
}
If you disable the SDK's ATT handling, resolve the prompt before initializing the ad SDK. Initializing first means the first ad requests go out without the IDFA even when the user later grants permission, which lowers fill on the initial screen.

3. SKAdNetwork identifiers 🔗

SKAdNetworkItems lets buyers attribute installs when the IDFA is unavailable; missing identifiers suppress demand from buyers that require SKAdNetwork. Apple reads this key only from the app's main Info.plist — it is not aggregated from frameworks or SDKs, so it must be added to your app even though the SDK depends on Google Mobile Ads.

Add the SKAdNetworkItems array with Google's published identifiers (Google's own cstr6suwn9.skadnetwork plus the participating third-party buyers). Copy the current, complete list from Google's Prepare privacy strategies page — Google updates it as buyers are added.

<key>SKAdNetworkItems</key>
<array>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>cstr6suwn9.skadnetwork</string>
    </dict>
    <!-- … plus the remaining identifiers from Google's list … -->
</array>

Identifiers must be lowercase. If you add mediation partners, also include any identifiers those partners require.

4. app-ads.txt on your website 🔗

Host an app-ads.txt file at the root of the developer/website domain listed on your app's App Store page (for example, https://example.com/app-ads.txt). It authorizes the buyers and exchanges that may sell your inventory; a missing or incomplete file causes most programmatic demand to be filtered out. Ezoic provides the required entries — confirm the file is published and current.

What the SDK does vs. what you must add 🔗

Item Provided automatically by the SDK You must add
Google Mobile Ads + Prebid SDKs Yes (transitive dependencies) —
Ad unit, sizes, targeting, Prebid config Yes (fetched from Ezoic) —
GADApplicationIdentifier / GADIsAdManagerApp No (per-app value) Yes, in Info.plist
ATT prompt (request + wait before ads) Yes (SDK presents it during initialize) —
NSUserTrackingUsageDescription string No (App Store privacy requirement) Yes, in Info.plist
SKAdNetworkItems No (Apple reads app Info.plist only) Yes, in Info.plist
app-ads.txt No (hosted on your domain) Yes, on your website

Apple and Google deliberately require these to live in the app bundle or on the publisher domain, so they cannot be bundled inside the SDK. Keep them in mind whenever you ship a new app.

The SDK can automatically read consent signals from UserDefaults when they are set by a consent management platform.

  • TCF v2 consent is read from IABTCF_* keys.
  • GPP consent is read from IABGPP_* keys.
  • US Privacy consent is read from the standard IAB privacy key.

You can also set consent manually:

EzoicAds.shared.setGDPRConsent(
    applies: true,
    consentString: "TCF_CONSENT_STRING"
)

EzoicAds.shared.setGPPConsent(
    gppString: "GPP_STRING",
    sectionIds: "7"
)

EzoicAds.shared.setSubjectToCOPPA(true)

Pageview Tracking 🔗

The SDK automatically tracks view controller navigation after initialization.

You can also track a pageview manually when a user navigates to a new screen or content view:

EzoicAds.shared.trackPageview { success in
    print("Pageview tracked: \(success)")
}

For apps using async/await on iOS 15 or higher:

let success = await EzoicAds.shared.trackPageview()

Troubleshooting 🔗

SDK Not Initializing 🔗

  1. Confirm the configured domain matches your Ezoic dashboard.
  2. Confirm the app has network access.
  3. Enable debugEnabled: true and review SDK logs in the Xcode console.

Ads Not Loading 🔗

  1. Initialize the SDK before calling loadAd().
  2. Confirm the Ezoic ad unit identifier is configured in Ezoic.
  3. Confirm the Google Mobile Ads application ID is present in Info.plist.
  4. Check consent configuration if traffic is subject to privacy regulations.