Skip to main content
Version: 6.x.x

App Open Ads

Overview

App open ads are full-screen ads designed to appear during app launch moments. They are similar to interstitial ads but are specifically tailored for two key scenarios:

Cold start — When the user opens the app fresh (the app was not previously in memory). A splash or loading screen is typically shown before the ad.

Soft launch — When the user returns to the app from the background or unlocks the device while the app is in the foreground. The app is still in memory but was suspended.

In both cases, the ad is displayed before the user reaches the main content. Users can dismiss the ad at any time.

Implementation Steps

At a high level, integrating app open ads involves the following:

  1. Build a manager class that preloads an ad so it's ready when needed.
  2. Display the ad when the app enters the foreground.
  3. React to ad lifecycle and presentation callbacks.

Best Practices

App open ads are a great way to monetize your app's loading screen, but it's important to follow best practices so that your users continue to enjoy using your app:

  • Show ads only during natural waiting moments. App open ads work best when users are already expecting a brief pause, such as during app launch or when returning from the background. Avoid surprising users with ads at unexpected times.
  • Always show a splash or loading screen first. The user should never see actual app content before the ad appears. The expected flow is: splash screen → app open ad → app content.
  • Initialize the SDK before loading ads. Make sure the BlueStack SDK has fully initialized before you attempt to load an app open ad. Loading ads before initialization may result in failed requests.
  • Preload the ad early. Load the ad as soon as possible so there is no delay when it's time to show it. Avoid loading other ad formats in parallel, as this can strain device resources and reduce fill rates.
  • Respect user experience with frequency controls. Avoid showing an ad on every single app open. Consider strategies such as:
    • Showing an ad on every second or third opportunity instead of every time.
    • Requiring a minimum background duration (e.g., 30 seconds or 2 minutes) before showing an ad on soft launch.
    • Skipping soft launch ads for a period after showing a cold start ad.
  • Be mindful of new users. Hold off on showing app open ads until users have opened and used your app a few times. This helps build a positive first impression before introducing ads.
  • Handle ad expiration. Preloaded ads expire after a certain period (typically 4 hours). Always verify that the ad is still valid before attempting to show it, and reload if it has expired.
  • Coordinate your loading screen with the ad. If you have a loading screen running behind the app open ad and it finishes before the user dismisses the ad, dismiss the loading screen in the onAdDismissed callback to ensure a smooth transition to your app content.

Create an App Open Ad

Implement a Manager Class

App open ads should appear immediately when the user opens or returns to your app, so it's important to have the ad loaded and ready before it's needed. The best approach is to create a manager class that takes care of loading ads ahead of time, checking whether a loaded ad is still valid, and displaying it at the right moment.

Create a class called AppOpenAdManager with an AppOpenAdManagerDelegate protocol to get notified when the ad flow completes:

#import <BlueStackSDK/BlueStackSDK.h>

@protocol AppOpenAdManagerDelegate <NSObject>
- (void)appOpenAdDidComplete:(AppOpenAdManager *)appOpenAdManager;
@end

@interface AppOpenAdManager : NSObject <BLSAppOpenAdDelegate, FullScreenDelegate>

@property (class, nonatomic, readonly) AppOpenAdManager *shared;
@property (nonatomic, strong, nullable) BLSAppOpenAd *appOpenAd;
@property (nonatomic, weak, nullable) id<AppOpenAdManagerDelegate> appOpenAdManagerDelegate;
@property (nonatomic, assign) BOOL isLoadingAd;
@property (nonatomic, assign) BOOL isShowingAd;
@property (nonatomic, strong, nullable) NSDate *loadTime;
@property (nonatomic, assign) NSTimeInterval timeoutInterval;

@end

@implementation AppOpenAdManager

+ (AppOpenAdManager *)shared {
static AppOpenAdManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[AppOpenAdManager alloc] init];
});
return sharedInstance;
}

- (instancetype)init {
self = [super init];
if (self) {
_isLoadingAd = NO;
_isShowingAd = NO;
_timeoutInterval = 4 * 3600;
}
return self;
}

@end

Standalone Creation

To create an app open ad, instantiate an instance of AppOpenAd with a placement Id.


#import <BlueStackSDK/BlueStackSDK.h>

@interface AppDelegate ()

@property (strong, nonatomic) BLSAppOpenAd *appOpenAd;

@end

@implementation AppDelegate

self.appOpenAd = [[BLSAppOpenAd alloc] initWithPlacementID:@"APP_OPEN_PLACEMENT_ID"];

@end

Load an App Open Ad

With AppOpenAdManager

The recommended way to load an app open ad is through the AppOpenAdManager class. To load an app open ad, call the loadAppOpenAd() method. The method guards against duplicate requests — it won't start a new load if one is already in progress or if a valid (non-expired) ad is already available.

- (void)loadAppOpenAd {
if (self.isLoadingAd || [self isAppOpenAdAvailableToShow]) {
return;
}

self.isLoadingAd = YES;

// Clean up any existing app open ad instance before creating a new one
if (self.appOpenAd) {
self.appOpenAd = nil;
}

// Create an AppOpenAd instance with your placement ID
// The placement ID identifies the ad unit in the BlueStack dashboard
self.appOpenAd = [[BLSAppOpenAd alloc] initWithPlacementID:@"APP_OPEN_PLACEMENT_ID"];

// Set the AppOpenAdDelegate to receive ad lifecycle callbacks
// This delegate handles onAdLoaded and onAdFailedToLoad events
self.appOpenAd.delegate = self;

// Set the FullScreenDelegate to receive display-related callbacks
// This delegate handles onAdDisplayed, onAdFailedToDisplay, onAdClicked, and onAdDismissed events
self.appOpenAd.fullScreenDelegate = self;

/// Creates and returns RequestOptions for ad targeting
/// These parameters help deliver more relevant ads to users
RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25)
location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453]
gender:GenderMale
keyword:@"brand=myBrand;category=sport"

// Load the app open ad with optional RequestOptions
// The load() method initiates an ad request to the ad server
// RequestOptions are optional - pass nil if you don't need targeting parameters
// The delegate will receive onAdLoaded() or onAdFailedToLoad() callbacks
contentUrl:@"https://my_content_url.com/"];
[self.appOpenAd loadWithRequestOptions:requestOptions];
}

Standalone Loading

App open ad can be loaded by calling load(requestOptions:) method with a RequestOptions instance for supplying targeting information or simply calling load() method.

RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25)
location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453]
gender:GenderMale
keyword:@"brand=myBrand;category=sport"
contentUrl:@"https://my_content_url.com/"];
[self.appOpenAd loadWithRequestOptions:requestOptions];
// Or
// [self.appOpenAd load];
info

Ad load call will preload ad before you show it so that ads can be shown with zero latency when needed. This preloaded ad will expire after certain period. If you try to show an expired ad, you will get AdErrorAdExpired error on func onAdFailedToDisplay(_ ad: any FullScreenDisplayableAd, _ error: any Error) callback. Once the ad get expired, you can call load again using existing instance to preload a new ad.

Show an Ad

With AppOpenAdManager

Before showing the ad, the manager checks whether an ad is already on screen and whether a valid ad is available. If no ad is ready, it notifies the delegate and triggers a new load so an ad will be available next time.

- (void)showAppOpenAdIfAvailable {
if (self.isShowingAd) {
NSLog(@"App open ad is already showing.");
return;
}

if (![self isAppOpenAdAvailableToShow]) {
NSLog(@"App open ad is not ready yet.");
[self.appOpenAdManagerDelegate appOpenAdDidComplete:self];
[self loadAppOpenAd];
return;
}

// Check if the ad is ready and show it
// Ensure if the ad is ready to be displayed using isReady() before calling show()
// The show() method presents the full-screen app open ad
// The FullScreenDelegate callbacks will be triggered during the ad lifecycle
dispatch_async(dispatch_get_main_queue(), ^{
if (self.appOpenAd.isReady) {
[self.appOpenAd showFromRootViewController:self];
}
});
self.isShowingAd = YES;
}

Standalone Showing

To show an app open ad directly, check isReady on the AppOpenAd instance and call show(fromRootViewController:) with an optional root view controller.

if (self.appOpenAd.isReady) {
[self.appOpenAd showFromRootViewController:self];
}

Show the Ad During App Foregrounding

To display the ad whenever the user returns to your app, call showAppOpenAdIfAvailable() from your AppDelegate's applicationDidBecomeActive: method. This ensures the ad is shown (or a new one is loaded) each time the app comes to the foreground.

- (void)applicationDidBecomeActive:(UIApplication *)application {
[AppOpenAdManager.shared showAppOpenAdIfAvailable];
}
tip

If your app uses Scenes, implement sceneDidBecomeActive: in your UISceneDelegate instead of applicationDidBecomeActive:.

Destroying an App Open Ad

You need to keep a strong reference to the AppOpenAd instance. You can release it when you are done showing the app open ad (app open ad has dismissed) simply by setting it to nil.

self.appOpenAd = nil;

Ad events

Register for app open events

AppOpenAd delivers lifecycle and presentation events through two delegate protocols: AppOpenAdDelegate for load-related events and FullScreenDelegate for display-related events.

self.appOpenAd.delegate = self;
self.appOpenAd.fullScreenDelegate = self;

App Open ad lifecycle events

AppOpenAdDelegate notifies you when the ad has finished loading or when a load attempt fails. On a successful load, record the load time so you can later check for expiration. On failure, clean up the ad reference and reset state.

#pragma mark - AppOpenAdDelegate

- (void)didLoadAppOpenAd:(AppOpenAd *)ad {
NSLog(@"App open ad loaded");
self.isLoadingAd = NO;
self.loadTime = [NSDate date];
}

- (void)appOpenAd:(AppOpenAd *)ad didFailedToLoadWithError:(NSError *)error {
NSLog(@"Failed to load app open ad with error: %@", error.localizedDescription);
self.isLoadingAd = NO;
self.appOpenAd = nil;
self.loadTime = nil;
}

App Open ad full-screen events

FullScreenDelegate reports when the ad is displayed, clicked, dismissed, or fails to display. After the ad is dismissed or fails to display, release the current ad, reset state, notify the delegate, and immediately start loading a new ad so one is ready for the next app foregrounding.

#pragma mark - FullScreenDelegate

- (void)didDisplayAd:(id<FullScreenDisplayableAd>)ad {
NSLog(@"App open ad displayed");
}

- (void)ad:(id<FullScreenDisplayableAd>)ad didFailedToDisplayWithError:(NSError *)error {
NSLog(@"Failed to display app open ad with error: %@", error.localizedDescription);
self.appOpenAd = nil;
self.isShowingAd = NO;
[self.appOpenAdManagerDelegate appOpenAdDidComplete:self];
[self loadAppOpenAd];
}

- (void)didClickAd:(id<FullScreenDisplayableAd>)ad {
NSLog(@"App open ad clicked.");
}

- (void)didDismissAd:(id<FullScreenDisplayableAd>)ad {
NSLog(@"App open ad dismissed");
self.appOpenAd = nil;
self.isShowingAd = NO;
[self.appOpenAdManagerDelegate appOpenAdDidComplete:self];
[self loadAppOpenAd];
}

Handling Cold Starts with Loading Screens

The examples above focus on showing app open ads when users return to an app that is already suspended in memory (soft launch). Cold starts — when the app is launched fresh and was not previously in memory — require additional consideration.

During a cold start, there is no previously loaded ad ready to show immediately. The delay between requesting an ad and receiving one can create a situation where the user briefly sees app content before an ad unexpectedly appears. This is a poor user experience and should be avoided.

The recommended approach is to use a loading or splash screen during the app's startup sequence and only show the app open ad while that screen is still visible. Follow these guidelines:

  • Keep the loading screen visible until the ad is ready. Do not dismiss it or transition to app content before the ad has been shown.
  • Show the ad from the loading screen only. If your app finishes loading and has already moved the user to the main content, do not show the ad — the moment has passed.
  • Dismiss the loading screen in onAdDismissed. If the loading screen completes before the user dismisses the ad, wait for the onAdDismissed callback before transitioning to app content. This ensures a smooth flow from splash screen → ad → app content with no flicker or content flash in between.

Consider Ad Expiration

A preloaded ad can become stale if too much time passes between loading and displaying. To prevent showing an expired ad, track when the ad was loaded and verify its age before attempting to display it.

The AppOpenAdManager above stores a loadTime property that is set when the ad finishes loading. Two helper methods use this timestamp to determine whether the ad is still valid:

  • hasNotExpired(within:) — Returns true if the time elapsed since loadTime is less than the configured timeoutInterval (4 hours by default).
  • isAppOpenAdAvailableToShow() — Returns true only if an ad instance exists and has not expired.

The showAppOpenAdIfAvailable() method calls isAppOpenAdAvailableToShow() before every display attempt. If the ad has expired, it skips the display, notifies the delegate, and immediately starts loading a fresh ad so one is ready for the next opportunity.

Control Ad Frequency

To maintain a positive user experience, avoid showing an app open ad on every single foreground event. Consider implementing frequency controls such as:

  • Skip opportunities — Show an ad on every second or third app open instead of every time.
  • Minimum background duration — Only show an ad if the user was away from the app for a certain amount of time (e.g., 30 seconds, 2 minutes, or 15 minutes).
  • Cooldown after cold start — If you showed an ad during a cold start, skip soft launch ads for a set period afterward.
  • Frequency caps — Limit the total number of app open ads shown per session or per day. Where possible, tailor caps based on user cohorts or engagement levels.