Skip to main content

App Open Ads

App open ads appear when users bring your app to the foreground, providing a monetization opportunity during app launches.

Each ad format uses a Zone ID to identify the ad placement. Zone IDs are configured in the Empower dashboard.

Note: All ad status listeners are optional. The SDK handles ad loading and display automatically. Use listeners only if you need to track ad states for analytics, UI updates, or custom logic.


Quick Start

import EmpowerMobileAds

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?
private let zoneId = "YOUR_APP_OPEN_ZONE_ID"

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

// Initialize the SDK
EMAManager.shared.initialize(appAdIdentifier: "your_app_identifier")

// Preload the App Open ad
EMAManager.shared.loadAppOpen(zoneId: zoneId)

return true
}

func applicationDidBecomeActive(_ application: UIApplication) {
// Show the ad when the app becomes active
if EMAManager.shared.isAppOpenReady(zoneId: zoneId),
let rootVC = window?.rootViewController {
EMAManager.shared.showAppOpen(zoneId: zoneId, from: rootVC)
}
// Preload for next time
EMAManager.shared.loadAppOpen(zoneId: zoneId)
}
}

Implementation

Only show an App Open ad when the user returns after a meaningful time in the background, and never on the very first cold start (let your content load first).

AppDelegate

import EmpowerMobileAds

@main
class AppDelegate: UIResponder, UIApplicationDelegate, AdStatusDelegate {

var window: UIWindow?
private var backgroundTime: Date?
private let zoneId = "YOUR_APP_OPEN_ZONE_ID"

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

EMAManager.shared.initialize(appAdIdentifier: "your_app_identifier")
EMAManager.shared.loadAppOpen(zoneId: zoneId, delegate: self)
return true
}

func applicationDidEnterBackground(_ application: UIApplication) {
backgroundTime = Date()
}

func applicationDidBecomeActive(_ application: UIApplication) {
showAppOpenAdIfAppropriate()
}

private func showAppOpenAdIfAppropriate() {
if let backgroundTime = backgroundTime,
Date().timeIntervalSince(backgroundTime) >= 30, // at least 30 seconds
EMAManager.shared.isAppOpenReady(zoneId: zoneId),
let rootVC = window?.rootViewController {
EMAManager.shared.showAppOpen(zoneId: zoneId, from: rootVC)
}

backgroundTime = nil
// Always preload for next time
EMAManager.shared.loadAppOpen(zoneId: zoneId, delegate: self)
}

// MARK: - AdStatusDelegate (optional)

func empowerAppOpenStatusChanged(adStatus: AdStatus) {
switch adStatus {
case .ready:
print("App Open ad is ready")
case .failed:
print("App Open ad failed to load")
DispatchQueue.main.asyncAfter(deadline: .now() + 60) { [weak self] in
guard let self = self else { return }
EMAManager.shared.loadAppOpen(zoneId: self.zoneId, delegate: self)
}
case .shown:
print("App Open ad is showing")
case .skipped:
print("App Open ad dismissed")
default:
break
}
}
}

SceneDelegate (iOS 13+)

For apps using UISceneDelegate, wire the same logic into the scene lifecycle:

import EmpowerMobileAds

class SceneDelegate: UIResponder, UIWindowSceneDelegate, AdStatusDelegate {

var window: UIWindow?
private var backgroundTime: Date?
private let zoneId = "YOUR_APP_OPEN_ZONE_ID"

func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
EMAManager.shared.loadAppOpen(zoneId: zoneId, delegate: self)
}

func sceneDidEnterBackground(_ scene: UIScene) {
backgroundTime = Date()
}

func sceneDidBecomeActive(_ scene: UIScene) {
if let backgroundTime = backgroundTime,
Date().timeIntervalSince(backgroundTime) >= 30,
EMAManager.shared.isAppOpenReady(zoneId: zoneId),
let rootVC = window?.rootViewController {
EMAManager.shared.showAppOpen(zoneId: zoneId, from: rootVC)
}
backgroundTime = nil
EMAManager.shared.loadAppOpen(zoneId: zoneId, delegate: self)
}

func empowerAppOpenStatusChanged(adStatus: AdStatus) {
// Handle status changes if needed
}
}

Status Values

Swift (AdStatus)Objective-C (EMAAdStatusType)Description
.initializingEMAAdStatusTypeInitializingAd is loading
.readyEMAAdStatusTypeReadyAd is ready to show
.shownEMAAdStatusTypeShownAd is currently showing
.willLeaveEMAAdStatusTypeWillLeaveUser tapped the ad
.skippedEMAAdStatusTypeSkippedAd was dismissed / closed
.failed(Error?)EMAAdStatusTypeFailedAd failed to load

Note: App Open ads report dismissal as .skipped (EMAAdStatusTypeSkipped) — there is no .closed status for this format. If you reload from a status callback, do it on .skipped.

In Swift, adopt AdStatusDelegate and implement empowerAppOpenStatusChanged(adStatus:). In Objective-C, adopt EMAAdObserver and implement appOpenStatusChanged:.


Best Practices

  1. Don't show on every return — only after a meaningful background time (30+ seconds).
  2. Respect user experience — don't interrupt critical user flows.
  3. Reload after dismissal, not while showing — the SDK ignores a loadAppOpen call made while an ad is on screen (this prevents a second, unwanted app-open from queueing up behind the current one). Trigger the next preload from the .skipped status — or simply rely on the SDK, which auto-reloads after dismissal.
  4. Don't block app launch — let content load first on cold start.
  5. Skip during critical flows — don't show during checkout, onboarding, etc.
  6. Don't stack full-screen ads — an app-open shouldn't appear over an interstitial or rewarded ad that's already on screen. Before showing, check isAnyFullScreenAdShowing() and skip if another full-screen ad is up.

Troubleshooting

print("Ad ready: \(EMAManager.shared.isAppOpenReady(zoneId: "YOUR_ZONE_ID"))")
print("SDK initialized: \(EMAManager.shared.isSdkInitialized)")
EMASettings.shared.logLevel = .all

API Reference

func loadAppOpen(zoneId: String, delegate: AdStatusDelegate? = nil)
func isAppOpenReady(zoneId: String) -> Bool
func isAppOpenShowing(zoneId: String) -> Bool
func showAppOpen(zoneId: String, from viewController: UIViewController)

// true if ANY loaded full-screen ad (interstitial / rewarded / app open) is on screen —
// use it to avoid showing an app-open on top of another full-screen ad.
func isAnyFullScreenAdShowing() -> Bool

Objective-C selectors

SwiftObjective-C selector
loadAppOpen(zoneId:delegate:)loadAppOpenAdWithZoneId:observer:
isAppOpenReady(zoneId:)isAppOpenReadyForZoneId:
isAppOpenShowing(zoneId:)isAppOpenShowingForZoneId:
isAnyFullScreenAdShowing()isAnyFullScreenAdShowing
showAppOpen(zoneId:from:)showAppOpenWithZoneId:fromViewController: