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
- Swift
- Objective-C
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)
}
}
@import EmpowerMobileAds;
@implementation AppDelegate
static NSString *const appOpenZoneId = @"YOUR_APP_OPEN_ZONE_ID";
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize the SDK
[[EMAManager shared] initializeWithAppAdIdentifier:@"your_app_identifier"];
// Preload the App Open ad
[[EMAManager shared] loadAppOpenAdWithZoneId:appOpenZoneId observer:nil];
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
if ([[EMAManager shared] isAppOpenReadyForZoneId:appOpenZoneId]) {
UIViewController *rootVC = self.window.rootViewController;
[[EMAManager shared] showAppOpenWithZoneId:appOpenZoneId fromViewController:rootVC];
}
// Preload for next time
[[EMAManager shared] loadAppOpenAdWithZoneId:appOpenZoneId observer:nil];
}
@end
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
- Swift
- Objective-C
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
}
}
}
@import EmpowerMobileAds;
@interface AppDelegate () <EMAAdObserver>
@property (nonatomic, strong) NSDate *backgroundTime;
@end
@implementation AppDelegate
static NSString *const appOpenZoneId = @"YOUR_APP_OPEN_ZONE_ID";
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[EMAManager shared] initializeWithAppAdIdentifier:@"your_app_identifier"];
[[EMAManager shared] loadAppOpenAdWithZoneId:appOpenZoneId observer:self];
return YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
self.backgroundTime = [NSDate date];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self showAppOpenAdIfAppropriate];
}
- (void)showAppOpenAdIfAppropriate {
if (self.backgroundTime &&
[[NSDate date] timeIntervalSinceDate:self.backgroundTime] >= 30 &&
[[EMAManager shared] isAppOpenReadyForZoneId:appOpenZoneId]) {
UIViewController *rootVC = self.window.rootViewController;
[[EMAManager shared] showAppOpenWithZoneId:appOpenZoneId fromViewController:rootVC];
}
self.backgroundTime = nil;
[[EMAManager shared] loadAppOpenAdWithZoneId:appOpenZoneId observer:self];
}
#pragma mark - EMAAdObserver (optional)
- (void)appOpenStatusChanged:(EMAAdStatusType)status {
switch (status) {
case EMAAdStatusTypeReady:
NSLog(@"App Open ad is ready");
break;
case EMAAdStatusTypeFailed:
NSLog(@"App Open ad failed to load");
break;
case EMAAdStatusTypeShown:
NSLog(@"App Open ad is showing");
break;
case EMAAdStatusTypeSkipped:
NSLog(@"App Open ad dismissed");
break;
default:
break;
}
}
@end
SceneDelegate (iOS 13+)
For apps using UISceneDelegate, wire the same logic into the scene lifecycle:
- Swift
- Objective-C
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
}
}
@import EmpowerMobileAds;
@interface SceneDelegate () <EMAAdObserver>
@property (nonatomic, strong) NSDate *backgroundTime;
@end
@implementation SceneDelegate
static NSString *const appOpenZoneId = @"YOUR_APP_OPEN_ZONE_ID";
- (void)scene:(UIScene *)scene
willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions {
[[EMAManager shared] loadAppOpenAdWithZoneId:appOpenZoneId observer:self];
}
- (void)sceneDidEnterBackground:(UIScene *)scene {
self.backgroundTime = [NSDate date];
}
- (void)sceneDidBecomeActive:(UIScene *)scene {
if (self.backgroundTime &&
[[NSDate date] timeIntervalSinceDate:self.backgroundTime] >= 30 &&
[[EMAManager shared] isAppOpenReadyForZoneId:appOpenZoneId]) {
UIViewController *rootVC = self.window.rootViewController;
[[EMAManager shared] showAppOpenWithZoneId:appOpenZoneId fromViewController:rootVC];
}
self.backgroundTime = nil;
[[EMAManager shared] loadAppOpenAdWithZoneId:appOpenZoneId observer:self];
}
- (void)appOpenStatusChanged:(EMAAdStatusType)status {
// Handle status changes if needed
}
@end
Status Values
Swift (AdStatus) | Objective-C (EMAAdStatusType) | Description |
|---|---|---|
.initializing | EMAAdStatusTypeInitializing | Ad is loading |
.ready | EMAAdStatusTypeReady | Ad is ready to show |
.shown | EMAAdStatusTypeShown | Ad is currently showing |
.willLeave | EMAAdStatusTypeWillLeave | User tapped the ad |
.skipped | EMAAdStatusTypeSkipped | Ad was dismissed / closed |
.failed(Error?) | EMAAdStatusTypeFailed | Ad failed to load |
Note: App Open ads report dismissal as
.skipped(EMAAdStatusTypeSkipped) — there is no.closedstatus 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
- Don't show on every return — only after a meaningful background time (30+ seconds).
- Respect user experience — don't interrupt critical user flows.
- Reload after dismissal, not while showing — the SDK ignores a
loadAppOpencall 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.skippedstatus — or simply rely on the SDK, which auto-reloads after dismissal. - Don't block app launch — let content load first on cold start.
- Skip during critical flows — don't show during checkout, onboarding, etc.
- 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
- Swift
- Objective-C
print("Ad ready: \(EMAManager.shared.isAppOpenReady(zoneId: "YOUR_ZONE_ID"))")
print("SDK initialized: \(EMAManager.shared.isSdkInitialized)")
EMASettings.shared.logLevel = .all
NSLog(@"Ad ready: %d", [[EMAManager shared] isAppOpenReadyForZoneId:@"YOUR_ZONE_ID"]);
NSLog(@"SDK initialized: %d", [[EMAManager shared] isSdkInitialized]);
EMASettings.shared.logLevel = LogLevelAll;
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
| Swift | Objective-C selector |
|---|---|
loadAppOpen(zoneId:delegate:) | loadAppOpenAdWithZoneId:observer: |
isAppOpenReady(zoneId:) | isAppOpenReadyForZoneId: |
isAppOpenShowing(zoneId:) | isAppOpenShowingForZoneId: |
isAnyFullScreenAdShowing() | isAnyFullScreenAdShowing |
showAppOpen(zoneId:from:) | showAppOpenWithZoneId:fromViewController: |