# Appodeal Help Center
> Appodeal SDK integration guides for Android, iOS, and Unity. Ad types, ad network setup, data protection, analytics services, troubleshooting, and reporting API.
This file contains all documentation content in a single document following the llmstxt.org standard.
## Android
# Get Started
| Release Version | Release Date |
|-----------------|--------------|
| { getReleaseVersion("android") } | { getReleaseDate("android") } |
Follow this guide to get the best out of Appodeal.
The Appodeal SDK gives you **access to 70+ Ad Demand Sources and makes them compete
against each other in a real-time auction**, maximizing your ad revenues.
The Appodeal SDK also provides _In-app Bidding, Automatic UA Optimization,
User Segmentation & A/B Testing, Cross-Promotion and Direct Deals, Instant Payouts_,
and [much](https://appodeal.com/monetization/) [more](/faq-and-troubleshooting/faq/ad-mediation/getting-started-with-ad-mediation).
:::info
Appodeal SDK provides **two** ways of integration. From the options below, choose the one that fits your needs better:
:::
**The Appodeal SDK Full Package** - The Appodeal SDK provides you with tools to grow your mobile apps and games.
In addition to the monetization services, you can benefit from UA (User Acquisition)
and in-app analytics services. Here is the list of services Appodeal SDK Full Package includes:
- [Get started with Appodeal](get-started) to gain access to **Monetization** and **Analytics**.
- Connect with [Adjust](./services/adjust) or [AppsFlyer](./services/appsflyer) to unlock **Attribution features**.
- Connect with [Meta](./services/meta) (_formerly known as facebook-core_) for **User Acquisition**.
- Connect with [Firebase](./services/firebase) for **Analytics** + remote config for **product A/B tests** and settings.
If you plan to run UA campaigns, want to analyze your metrics in our Appodeal's business intelligence tool without using MMP,
or want to use remote config for tests and settings, your option is - **The Appodeal SDK Full Package**.
**The Appodeal SDK Mediation only** - If you do not plan to run (_UA_) User Acquisition campaigns,
nor want to use Appodeal advanced analytics, we have created a lite version of our SDK, only with mediation.
During the integration, you will not be required to install any additional services apart from mediation.
This may speed up your integration process, and you can always upgrade to the Full Package whenever you're ready.
---------------
:::tip
Please follow this integration guide step by step and choose your integration option when needed.
:::
The following document shows how to integrate Appodeal in your Android project with your desired networks
via gradle build setup, and configure all your ad formats.
:::info Minimum requirements:
Android API level 24 (Android OS 7.0) or higher.
:::
You can use our **demo app** as a reference project.
## Step 1. Import SDK
:::warning Android min api version < 26
Apps with `minApiVersion` below 26 may encounter compatibility issues with GoogleAds Identifier 18.2.0.
**Solution:** Enable core library desugaring in your build.gradle. See our [troubleshooting guide](/faq-and-troubleshooting/troubleshooting/android-common-issues/desugaring) for step-by-step instructions.
:::
### Configure Build.gradle
:::info Configure ad types and ad networks
We provide a convenient and interactive way to customize and generate `build.gradle` code based on selected ad types, networks and services.
To configure your **build.gradle** file please visit [Configure Mediated Networks](advanced/configure-mediated-networks) page.
:::
Here is a base and recommended **build.gradle** setup:
:::info Configure ad types and ad networks
We provide a convenient and interactive way to customize and generate `build.gradle` code based on selected ad types, networks and services.
To configure your **build.gradle** file please visit [Configure Mediated Networks](advanced/configure-mediated-networks) page.
:::
Here is a base and recommended **build.gradle** setup:
Once that's done, save the file and perform the **Gradle sync**.
## Step 2. Set Up The Project
### Network Security Configuration
Android 9.0 (API 28) blocks cleartext (non-HTTPS) traffic by default,
which can prevent ads from serving correctly. Read more on this [**here**](https://developer.android.com/training/articles/security-config).
To prevent the android system from blocking http traffic, follow these steps:
1. Add the **Network Security Configuration** file to your **AndroidManifest.xml** :
```jsx title=AndroidManifest.xml showLineNumbers
```
2. In your **network_security_config.xml** file, add **base-config** that sets **cleartextTrafficPermitted** to **true** :
```jsx title=network_security_config.xml showLineNumbers
```
### Configure Admob Meta-data
:::note
Only if you use AdMob adapter.
:::
:::warning Important
Admob Bidding is now available since **Appodeal SDK 3.2.0**.
Don't forget to download our newest version of Admob Sync tool from this [page](https://amsa-updates.appodeal.com/) and perform sync.
You can read more about Admob Sync in our [guide](/networks-setup/ad-networks/network-connection/admob-sync).
:::
Add your AdMob app id to **meta-data** tag:
```jsx title=XML showLineNumbers
```
You may find the AdMob app id in your personal account on the AdMob page:

## Step 3. Initialize SDK
We recommended to call initialization method in your MainActivity - `onCreate` method only once in your whole app:
```kotlin showLineNumbers
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/// Any other pre-initialization
/// app specific logic
// highlight-start
Appodeal.initialize(
context = activity,
appKey = "APP_KEY",
adTypes = adTypes,
callback = object : ApdInitializationCallback {
override fun onInitializationFinished(errors: List?) {
// Appodeal initialization finished
}
}
)
// highlight-end
}
```
```java showLineNumbers
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/// Any other pre-initialization
/// app specific logic
// highlight-start
Appodeal.initialize(activity, "APP_KEY", adTypes, new ApdInitializationCallback() {
@Override
public void onInitializationFinished(@Nullable List errors) {
// Appodeal initialization finished
}
});
// highlight-end
}
```
- `context` - Activity of your application.
- Replace `APP_KEY` with the actual app key.
You can find it in the list of applications in your [personal account](https://app.appodeal.com/apps).
- `adTypes` - Ad formats. Initialize only those ad types you want to use in your app to avoid getting ad requests to unused ones.
Use the type codes below to set the preferred ad format:
- `Appodeal.INTERSTITIAL` for interstitial;
- `Appodeal.REWARDED_VIDEO` for rewarded videos;
- `Appodeal.NATIVE` for native ads;
- `Appodeal.BANNER` for banners;
- `Appodeal.MREC` for 300\*250 banners.
:::tip Ad types can be combined using `or` operator.
For example, `Appodeal.INTERSTITIAL or Appodeal.REWARDED_VIDEO`.
:::
- `callback` - Appodeal initialization callback. The initialization callback is used to determine the result of the Appodeal SDK initialization
and is called when the initialization process is completed.
If initialization succeeds, list of errors is `null`. Otherwise, it contains a list of `errors`.
## Step 4. Configure Ad Types
Appodeal SDK is now imported and you're ready to implement an ad. Appodeal offers a number of
different ad formats, so you can choose the one that best fits your app's user experience.
## Step 5. What's next
### Add Privacy Policy
Make sure to add Privacy Policy to your app on Google Play that links to
[**Appodeal's Privacy Policy**](https://www.appodeal.com/privacy-policy)
[**Google Play Developer Distribution Agreement**](https://play.google.com/about/developer-distribution-agreement.html)
:::danger
[**Google policy**](https://support.google.com/googleplay/android-developer/answer/9857753?hl=en),
location permissions may only be requested to provide features beneficial to the user and relevant
to the core functionality of the app. You cannot request access to location data for the sole purpose
of advertising or analytics. If you are not using location for the main functions of your app
* Remove location permission in your app by adding the following code to AndroidManifest.xml and under the tag.
```jsx title=AndroidManifest.xml showLineNumbers
```
* Update the app on Google Play. During the publishing process, make sure there are no location
warnings in Google Play Console.
If you are using location for the main functions of your app
* Fill out the Location permissions declaration form in [**Google Play Console**](https://play.google.com/console/u/0/developers/app/app-content/permission-declarations).
You can read more about the declaration form [**here**](https://support.google.com/googleplay/android-developer/answer/9799150?hl=en#zippy=%2Cwhere-do-i-find-the-declaration).
* Update the app on Google Play. During the publishing process, make sure there are no location warnings in Google Play Console.
:::
:::info
Some networks and 3rd party dependencies (related to network
dependencies) can include their own permissions to the manifest. If you
want to force remove such permissions you can refer to [**this guide**](https://developer.android.com/studio/build/manifest-merge#node_markers).
:::
### Add App-ads.txt File
The app-ads.txt file is a text file which provides a mechanism for publishers to declare their authorized digital sellers.
You can find detailed information [here](../../advanced/app-ads)
---
## Banner
Banner ads are classic static banners, usually located at the bottom or
top of the screen. Appodeal supports traditional 320x50 banners, 728x90
tablet banners and smart banners that adjust to the size and orientation
of the device.
You can use our **demo banner app** as a reference project.
## Fixed Positioned Banner
### Display Banner At The Specific Position Of The Screen
To display banner, you need to call the following code in the activity:
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.BANNER) // Display banner with last position or bottom position of the screen
Appodeal.show(this, Appodeal.BANNER_BOTTOM) // Display banner at the bottom of the screen
Appodeal.show(this, Appodeal.BANNER_TOP) // Display banner at the top of the screen
Appodeal.show(this, Appodeal.BANNER_LEFT) // Display banner at the left of the screen
Appodeal.show(this, Appodeal.BANNER_RIGHT) // Display banner at the right of the screen
```
```java showLineNumbers
Appodeal.show(this, Appodeal.BANNER); // Display banner with last position or bottom position of the screen
Appodeal.show(this, Appodeal.BANNER_BOTTOM); // Display banner at the bottom of the screen
Appodeal.show(this, Appodeal.BANNER_TOP); // Display banner at the top of the screen
Appodeal.show(this, Appodeal.BANNER_LEFT); // Display banner at the left of the screen
Appodeal.show(this, Appodeal.BANNER_RIGHT); // Display banner at the right of the screen
```
The method returns a **boolean** value indicating whether the call to the show method was passed to the appropriate SDK.
:::caution SDK can't show ads without a network connection!
:::
:::caution `BannerView` should be on the top of the hierarchy and can not be overlapped by other views.
:::
### Check If Ad Is Loaded
You can check if the ad has been loaded before showing it. This method
returns a boolean value indicating whether or not the banner has
been loaded.
```kotlin showLineNumbers
Appodeal.isLoaded(Appodeal.BANNER)
```
```java showLineNumbers
Appodeal.isLoaded(Appodeal.BANNER);
```
### Manual Caching
By default, auto caching is enabled: Appodeal SDK starts to load Banner right after the initialization method is called.
The next banner ad starts to load after the previous one has been closed.
To disable automatic caching for banner, use the code below before SDK initialization:
```kotlin showLineNumbers
Appodeal.setAutoCache(Appodeal.BANNER, false)
```
```java showLineNumbers
Appodeal.setAutoCache(Appodeal.BANNER, false);
```
To cache banner use:
```kotlin showLineNumbers
Appodeal.cache(this, Appodeal.BANNER)
```
```java showLineNumbers
Appodeal.cache(this, Appodeal.BANNER);
```
Read more on manual caching in our [**FAQ**](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
### Hide
```kotlin showLineNumbers
Appodeal.hide(this, Appodeal.BANNER)
```
```java showLineNumbers
Appodeal.hide(this, Appodeal.BANNER);
```
### Callbacks
```kotlin showLineNumbers
Appodeal.setBannerCallbacks(object : BannerCallbacks {
override fun onBannerLoaded(height: Int, isPrecache: Boolean) {
// Called when banner is loaded
}
override fun onBannerFailedToLoad() {
// Called when banner failed to load
}
override fun onBannerShown() {
// Called when banner is shown
}
override fun onBannerShowFailed() {
// Called when banner show failed
}
override fun onBannerClicked() {
// Called when banner is clicked
}
override fun onBannerExpired() {
// Called when banner is expired
}
})
```
```java showLineNumbers
Appodeal.setBannerCallbacks(new BannerCallbacks() {
@Override
public void onBannerLoaded(int height, boolean isPrecache) {
// Called when banner is loaded
}
@Override
public void onBannerFailedToLoad() {
// Called when banner failed to load
}
@Override
public void onBannerShown() {
// Called when banner is shown
}
@Override
public void onBannerShowFailed() {
// Called when banner show failed
}
@Override
public void onBannerClicked() {
// Called when banner is clicked
}
@Override
public void onBannerExpired() {
// Called when banner is expired
}
});
```
:::info
All callbacks are called on the main thread.
:::
## Custom Positioned Banner
### Display Banner In The Specified View In The Layout File
1. Add `com.appodeal.ads.BannerView` to your layout file:
```jsx title=layout.xml showLineNumbers
```
2. Set view id before the SDK initialization to show banner:
```kotlin showLineNumbers
Appodeal.setBannerViewId(R.id.appodealBannerView)
```
```java showLineNumbers
Appodeal.setBannerViewId(R.id.appodealBannerView);
```
3. Now you can show the banner in the view specified (Make sure that the required view is on the screen):
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.BANNER_VIEW)
```
```java showLineNumbers
Appodeal.show(this, Appodeal.BANNER_VIEW);
```
:::caution SDK can't show ads without a network connection!
:::
:::caution `BannerView` should be on the top of the hierarchy and can not be overlapped by other views.
:::
### Display Banner In Programmatically Created View
1. Create banner view and add view to your layout:
```kotlin showLineNumbers
val adView = Appodeal.getBannerView(this)
rootLayout.addView(adView)
```
```java showLineNumbers
BannerView adView = Appodeal.getBannerView(this);
rootLayout.addView(adView);
```
2. Now you can show the banner in the view specified (Make sure that the required view is on the screen):
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.BANNER_VIEW)
```
```java showLineNumbers
Appodeal.show(this, Appodeal.BANNER_VIEW);
```
:::caution SDK can't show ads without a network connection!
:::
:::caution `BannerView` should be on the top of the hierarchy and can not be overlapped by other views.
:::
## Advanced
### Use safe area
:::info
When a banner is attached to the screen, it is displayed by default relative to the safe area of the screen (camera hole, status and navigation bars).
:::
Displaying a banner relative to the safe area (default)
You can disable the display of the banner taking into account the safe area using the method:
```kotlin showLineNumbers
Appodeal.setUseSafeArea(false)
```
```java showLineNumbers
Appodeal.setUseSafeArea(false);
```
Displaying a banner regardless of the safe area
:::warning
If your application or activity does not run in fullscreen mode, it is not recommended to disable the safe area.
:::
Incorrect banner displaying when safe area is disabled
### Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.BANNER, "yourPlacementName")
```
```java showLineNumbers
Appodeal.show(this, Appodeal.BANNER, "yourPlacementName");
```
If the loaded ad can't be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement), check if the ad can be shown
before calling show method:
```kotlin showLineNumbers
if (Appodeal.canShow(Appodeal.BANNER, "yourPlacementName")) {
Appodeal.show(this, Appodeal.BANNER, "yourPlacementName")
}
```
```java showLineNumbers
if (Appodeal.canShow(Appodeal.BANNER, "yourPlacementName")) {
Appodeal.show(this, Appodeal.BANNER, "yourPlacementName");
}
```
You can configure your impression logic for each placement.
If you have no placements or call `Appodeal.show` with a placement that
does not exist, the impression will be tagged with `default` placement
with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
### Check If Ad Is Initialized
To check if banner was initialized, you can use the method:
```kotlin showLineNumbers
Appodeal.isInitialized(Appodeal.BANNER)
```
```java showLineNumbers
Appodeal.isInitialized(Appodeal.BANNER);
```
Returns `true`, if the banner was initialized.
### Check If Autocache Is Enabled
To check if autocache is enabled for banner, you can use the method:
```kotlin showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.BANNER)
```
```java showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.BANNER);
```
Returns `true`, if autocache is enabled for banner.
### Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```kotlin showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.BANNER)
```
```java showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.BANNER);
```
:::tip This method is reasonable to use if manual caching of ads is enabled.
:::
### Activity "Paused" State Handling
We’ll automatically handle pause and resume state for already displayed Banners on Activity on which
they were displayed, however, we don’t restore displayed Banners after Activity recreation (e.g. -
orientation changes) and we don’t show Banners in newly created Activities.
For display ads on newly created Activity just call `Appodeal.show()` method if required:
```kotlin showLineNumbers
override fun onResume() {
super.onResume()
Appodeal.show(this, Appodeal.BANNER)
}
```
```java showLineNumbers
@Override
public void onResume() {
super.onResume();
Appodeal.show(this, Appodeal.BANNER);
}
```
This behavior can be changed by calling
`Appodeal.setSharedAdsInstanceAcrossActivities(true)`
(See more: [**Enable Shared View Ads Instance Across Activities Logic**](#enable-shared-view-ads-instance-across-activities-logic))
### Destroy
If you want to hide the banner from all activities and clear the memory, call the code below:
```kotlin showLineNumbers
Appodeal.destroy(Appodeal.BANNER)
```
```java showLineNumbers
Appodeal.destroy(Appodeal.BANNER);
```
### Enable 728x90 Banners
To enable 728x90 banners, use the following method:
```kotlin showLineNumbers
Appodeal.set728x90Banners(true)
```
```java showLineNumbers
Appodeal.set728x90Banners(true);
```
:::info
The method will work if your device is larger than 7 inches
:::
### Enable Shared View Ads Instance Across Activities Logic
Appodeal SDK binds the `Banner/MREC` to the Activity which was passed to the `Appodeal.show` method.
To make it easier for you to manage View ads display logic across all Activities we added a new
method in Appodeal class:
```kotlin showLineNumbers
Appodeal.setSharedAdsInstanceAcrossActivities(sharedAdsInstanceAcrossActivities: Boolean)
```
```java showLineNumbers
Appodeal.setSharedAdsInstanceAcrossActivities(boolean sharedAdsInstanceAcrossActivities);
```
- When this method is used with the `true` parameter, the SDK will show AdView on all new
activities without calling additional code from your side.
- If you want to control the display yourself, you can call the method with the false parameter.
:::caution
In this case, this parameter is false, be careful with changing orientation or moving to a new activity,
the `Banner/MREC` will not be shown automatically, since it was bound to the previous activity.
If you want to hide the `Banner/MREC`, you need to call the `Appodeal.hide()` method with the
parameters of the activity to which the `Banner/MREC` was bound.
:::
You can also check the current state of this logic:
```kotlin showLineNumbers
Appodeal.isSharedAdsInstanceAcrossActivities()
```
```java showLineNumbers
Appodeal.isSharedAdsInstanceAcrossActivities();
```
:::info
Shared View Ads Instance Across Activities is **disabled by default**
:::
### Disable Banner Refresh Animation
To disable banner refresh animation, use:
```kotlin showLineNumbers
Appodeal.setBannerAnimation(false)
```
```java showLineNumbers showLineNumbers
Appodeal.setBannerAnimation(false);
```
:::info
Banner animation is **enabled by default**.
:::
### Disable Smart Banners
Smart banners are the banner ads that automatically fit the screen size. Using them helps to deal
with the increasing fragmentation of the screen sizes on different devices.
To disable them, use the following method:
```kotlin showLineNumbers
Appodeal.setSmartBanners(false)
```
```java showLineNumbers
Appodeal.setSmartBanners(false);
```
:::info
Smart banners is **enabled by default**.
:::
### Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the **Banner \[Notify Shown\]** log if show was tracked successfully.
```log showLineNumbers
Appodeal com.example.app D Banner [Notify Shown]
```
---
## Interstitial
Interstitial ads are full-screen advertisements.
You can use our **demo interstitial app** as a reference project.
## Check If Ad Is Loaded
You can check if the ad has been uploaded before showing it. This method returns a boolean value indicating whether the
intermediate element has been loaded or not.
```kotlin showLineNumbers
Appodeal.isLoaded(Appodeal.INTERSTITIAL)
```
```java showLineNumbers
Appodeal.isLoaded(Appodeal.INTERSTITIAL);
```
We recommend you always check whether an ad is available before trying to show it.
```kotlin showLineNumbers
if (Appodeal.isLoaded(Appodeal.INTERSTITIAL)) {
Appodeal.show(this, Appodeal.INTERSTITIAL)
}
```
```java showLineNumbers
if (Appodeal.isLoaded(Appodeal.INTERSTITIAL)) {
Appodeal.show(this, Appodeal.INTERSTITIAL);
}
```
## Display
To display interstitial, you need to call the following code in the activity:
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.INTERSTITIAL)
```
```java showLineNumbers
Appodeal.show(this, Appodeal.INTERSTITIAL);
```
:::caution SDK can't show ads without a network connection!
:::
The method returns a **boolean** value indicating whether the call to the show method was passed to the appropriate SDK.
## Manual Caching
By default, auto caching is enabled: Appodeal SDK starts to load Interstitial right after the initialization method is called.
The next interstitial ad starts to load after the previous one has been closed.
To disable automatic caching for interstitials, use the code below before SDK initialization:
```kotlin showLineNumbers
Appodeal.setAutoCache(Appodeal.INTERSTITIAL, false)
```
```java showLineNumbers
Appodeal.setAutoCache(Appodeal.INTERSTITIAL, false);
```
To cache interstitial use:
```kotlin showLineNumbers
Appodeal.cache(this, Appodeal.INTERSTITIAL)
```
```java showLineNumbers
Appodeal.cache(this, Appodeal.INTERSTITIAL);
```
Read more on manual caching in our [**FAQ**](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
## Callbacks
```kotlin showLineNumbers
Appodeal.setInterstitialCallbacks(object : InterstitialCallbacks {
override fun onInterstitialLoaded(isPrecache: Boolean) {
// Called when interstitial is loaded
}
override fun onInterstitialFailedToLoad() {
// Called when interstitial failed to load
}
override fun onInterstitialShown() {
// Called when interstitial is shown
}
override fun onInterstitialShowFailed() {
// Called when interstitial show failed
}
override fun onInterstitialClicked() {
// Called when interstitial is clicked
}
override fun onInterstitialClosed() {
// Called when interstitial is closed
}
override fun onInterstitialExpired() {
// Called when interstitial is expired
}
})
```
```java showLineNumbers
Appodeal.setInterstitialCallbacks(new InterstitialCallbacks() {
@Override
public void onInterstitialLoaded(boolean isPrecache) {
// Called when interstitial is loaded
}
@Override
public void onInterstitialFailedToLoad() {
// Called when interstitial failed to load
}
@Override
public void onInterstitialShown() {
// Called when interstitial is shown
}
@Override
public void onInterstitialShowFailed() {
// Called when interstitial show failed
}
@Override
public void onInterstitialClicked() {
// Called when interstitial is clicked
}
@Override
public void onInterstitialClosed() {
// Called when interstitial is closed
}
@Override
public void onInterstitialExpired() {
// Called when interstitial is expired
}
});
```
:::info
All callbacks are called on the main thread.
:::
## Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.INTERSTITIAL, "yourPlacementName")
```
```java showLineNumbers
Appodeal.show(this, Appodeal.INTERSTITIAL, "yourPlacementName");
```
If the loaded ad can't be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement), check if the ad can be shown
before calling show method:
```kotlin showLineNumbers
if (Appodeal.canShow(Appodeal.INTERSTITIAL, "yourPlacementName")) {
Appodeal.show(this, Appodeal.INTERSTITIAL, "yourPlacementName")
}
```
```java showLineNumbers
if (Appodeal.canShow(Appodeal.INTERSTITIAL, "yourPlacementName")) {
Appodeal.show(this, Appodeal.INTERSTITIAL, "yourPlacementName");
}
```
You can configure your impression logic for each placement.
If you have no placements or call `Appodeal.show` with a placement that
does not exist, the impression will be tagged with `default` placement
with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
## Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```kotlin showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.INTERSTITIAL)
```
```java showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.INTERSTITIAL);
```
:::tip This method is reasonable to use if manual caching of ads is enabled.
:::
## Check If Ad Is Initialized
To check if interstitial was initialized, you can use the method:
```kotlin showLineNumbers
Appodeal.isInitialized(Appodeal.INTERSTITIAL)
```
```java showLineNumbers
Appodeal.isInitialized(Appodeal.INTERSTITIAL);
```
Returns `true`, if the interstitial was initialized.
## Check If Autocache Is Enabled
To check if autocache is enabled for interstitial, you can use the method:
```kotlin showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.INTERSTITIAL)
```
```java showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.INTERSTITIAL);
```
Returns `true`, if autocache is enabled for interstitial.
## Mute Videos If Call Volume Is Muted
You can mute the sound in a video interstitial using the method:
```kotlin showLineNumbers
Appodeal.muteVideosIfCallsMuted(true)
```
```java showLineNumbers
Appodeal.muteVideosIfCallsMuted(true);
```
:::note
This method works if the user's device has silent mode or only vibration enabled.
:::
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the **Interstitial \[Notify Shown\]** log if show was tracked successfully.
```log showLineNumbers
Appodeal com.example.app D Interstitial [Notify Shown]
```
---
## MREC
MREC is 300x250 banner. This type can be useful if the application has a large free area for placing a banner in the interface.
You can use our **demo MREC app** as a reference project.
## Check If Ad Is Loaded
You can check if the ad has been loaded before showing it. This method
returns a boolean value indicating whether or not the MREC has
been loaded.
```kotlin showLineNumbers
Appodeal.isLoaded(Appodeal.MREC)
```
```java showLineNumbers
Appodeal.isLoaded(Appodeal.MREC);
```
We recommend you always check whether an ad is available before trying to show it.
```kotlin showLineNumbers
if (Appodeal.isLoaded(Appodeal.MREC)) {
Appodeal.show(this, Appodeal.MREC)
}
```
```java showLineNumbers
if (Appodeal.isLoaded(Appodeal.MREC)) {
Appodeal.show(this, Appodeal.MREC);
}
```
## Display
To display MREC, you need to call the following code in the activity:
1. Add `com.appodeal.ads.MrecView` to your layout file:
```jsx title=layout.xml showLineNumbers
```
2. Set view id before the SDK initialization to show MREC:
```kotlin showLineNumbers
Appodeal.setMrecViewId(R.id.appodealMrecView)
```
```java showLineNumbers
Appodeal.setMrecViewId(R.id.appodealMrecView);
```
3. Now you can show the MREC in the view specified (Make sure that the required view is on the screen):
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.MREC)
```
```java showLineNumbers
Appodeal.show(this, Appodeal.MREC);
```
The method returns a **boolean** value indicating whether the call to the show method was passed to the appropriate SDK.
:::caution SDK can't show ads without a network connection!
:::
:::caution `MrecView` should be on the top of the hierarchy and can not be overlapped by other views.
:::
## Manual Caching
By default, auto caching is enabled: Appodeal SDK starts to load MREC right after the initialization method is called.
The next MREC ad starts to load after the previous one has been closed.
To disable automatic caching for MREC, use the code below before SDK initialization:
```kotlin showLineNumbers
Appodeal.setAutoCache(Appodeal.MREC, false)
```
```java showLineNumbers
Appodeal.setAutoCache(Appodeal.MREC, false);
```
To cache MREC use:
```kotlin showLineNumbers
Appodeal.cache(this, Appodeal.MREC)
```
```java showLineNumbers
Appodeal.cache(this, Appodeal.MREC);
```
Read more on manual caching in our [**FAQ**](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
## Display MREC In Programmatically Created View
1. Create MREC view and add view to your layout:
```kotlin showLineNumbers
val adView = Appodeal.getMrecView(this)
rootLayout.addView(adView)
```
```java showLineNumbers
MrecView adView = Appodeal.getMrecView(this);
rootLayout.addView(adView);
```
2. Now you can show the MrecView in the view specified (Make sure that the required view is on the screen):
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.MREC)
```
```java showLineNumbers
Appodeal.show(this, Appodeal.MREC);
```
The method returns a **boolean** value indicating whether the call to the show method was passed to the appropriate SDK.
:::caution SDK can't show ads without a network connection!
:::
:::caution `MrecView` should be on the top of the hierarchy and can not be overlapped by other views.
:::
## Callbacks
```kotlin showLineNumbers
Appodeal.setMrecCallbacks(object : MrecCallbacks {
override fun onMrecLoaded(isPrecache: Boolean) {
// Called when MREC is loaded
}
override fun onMrecFailedToLoad() {
// Called when MREC failed to load
}
override fun onMrecShown() {
// Called when MREC is shown
}
override fun onMrecShowFailed() {
// Called when MREC show failed
}
override fun onMrecClicked() {
// Called when MREC is clicked
}
override fun onMrecExpired() {
// Called when MREC is expired
}
})
```
```java showLineNumbers
Appodeal.setMrecCallbacks(new MrecCallbacks() {
@Override
public void onMrecLoaded(boolean isPrecache) {
// Called when MREC is loaded
}
@Override
public void onMrecFailedToLoad() {
// Called when MREC failed to load
}
@Override
public void onMrecShown() {
// Called when MREC is shown
}
@Override
public void onMrecShowFailed() {
// Called when MREC show failed
}
@Override
public void onMrecClicked() {
// Called when MREC is clicked
}
@Override
public void onMrecExpired() {
// Called when MREC is expired
}
});
```
:::info
All callbacks are called on the main thread.
:::
## Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.MREC, "yourPlacementName")
```
```java showLineNumbers
Appodeal.show(this, Appodeal.MREC, "yourPlacementName");
```
If the loaded ad can't be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement), check if the ad can be shown
before calling show method:
```kotlin showLineNumbers
if (Appodeal.canShow(Appodeal.MREC, "yourPlacementName")) {
Appodeal.show(this, Appodeal.MREC, "yourPlacementName")
}
```
```java showLineNumbers
if (Appodeal.canShow(Appodeal.MREC, "yourPlacementName")) {
Appodeal.show(this, Appodeal.MREC, "yourPlacementName");
}
```
You can configure your impression logic for each placement.
If you have no placements or call `Appodeal.show` with a placement that
does not exist, the impression will be tagged with `default` placement
with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
## Check If Ad Is Initialized
To check if MREC was initialized, you can use the method:
```kotlin showLineNumbers
Appodeal.isInitialized(Appodeal.MREC)
```
```java showLineNumbers
Appodeal.isInitialized(Appodeal.MREC);
```
Returns `true`, if the MREC was initialized.
## Check If Autocache Is Enabled
To check if autocache is enabled for MREC, you can use the method:
```kotlin showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.MREC)
```
```java showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.MREC);
```
Returns `true`, if autocache is enabled for MREC.
## Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```kotlin showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.MREC)
```
```java showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.MREC);
```
:::tip This method is reasonable to use if manual caching of ads is enabled.
:::
## Activity "Paused" State Handling
We’ll automatically handle pause and resume state for already displayed MRECs on Activity on which
they were displayed, however, we don’t restore displayed MRECs after Activity recreation (e.g. -
orientation changes) and we don’t show MRECs in newly created Activities.
For display ads on newly created Activity just call `Appodeal.show()` method if required:
```kotlin showLineNumbers
override fun onResume() {
super.onResume()
Appodeal.show(this, Appodeal.MREC)
}
```
```java showLineNumbers
@Override
public void onResume() {
super.onResume();
Appodeal.show(this, Appodeal.MREC);
}
```
This behavior can be changed by calling
`Appodeal.setSharedAdsInstanceAcrossActivities(true)`
(See more: [**Enable Shared View Ads Instance Across Activities Logic**](#enable-shared-view-ads-instance-across-activities-logic))
## Hide
```kotlin showLineNumbers
Appodeal.hide(this, Appodeal.MREC)
```
```java showLineNumbers
Appodeal.hide(this, Appodeal.MREC);
```
## Destroy
If you want to hide the MREC from all activities and clear the memory, call the code below:
```kotlin showLineNumbers
Appodeal.destroy(Appodeal.MREC)
```
```java showLineNumbers
Appodeal.destroy(Appodeal.MREC);
```
## Enable Shared View Ads Instance Across Activities Logic
Appodeal SDK binds the `Banner/MREC` to the Activity which was passed to the `Appodeal.show` method.
To make it easier for you to manage View ads display logic across all Activities we added a new
method in Appodeal class:
```kotlin showLineNumbers
Appodeal.setSharedAdsInstanceAcrossActivities(sharedAdsInstanceAcrossActivities: Boolean)
```
```java showLineNumbers
Appodeal.setSharedAdsInstanceAcrossActivities(boolean sharedAdsInstanceAcrossActivities);
```
- When this method is used with the `true` parameter, the SDK will show AdView on all new
activities without calling additional code from your side.
- If you want to control the display yourself, you can call the method with the `false` parameter.
:::caution
In this case, this parameter is false, be careful with changing orientation or moving to a new activity,
the `Banner/MREC` will not be shown automatically, since it was bound to the previous activity.
If you want to hide the `Banner/MREC`, you need to call the `Appodeal.hide()` method with the
parameters of the activity to which the `Banner/MREC` was bound.
:::
You can also check the current state of this logic:
```kotlin showLineNumbers
Appodeal.isSharedAdsInstanceAcrossActivities()
```
```java showLineNumbers
Appodeal.isSharedAdsInstanceAcrossActivities();
```
:::info
Shared View Ads Instance Across Activities is **disabled by default**
:::
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the **Mrec \[Notify Shown\]** log if show was tracked successfully.
```log showLineNumbers
Appodeal com.example.app D Mrec [Notify Shown]
```
---
## Native
Native ad is a flexible type of advertising. You can adapt the display to your UI by preparing a template.
:::info
Appodeal provides 4 options to implement the layout of native ads **3 templates** + your **custom
implementation**
All of them are inherited from the same `NativeAdView` class.
`NativeAdView` consists of the following components:
1. `NativeIconView` - Icon of the native ad.
2. `AdAttributionView` - Advertising Indicator. This is a TextView labeled "Ad".
3. `TitleVIew` - Title of the native ad.
4. `DescriptionView` - Text descriptionView of the native ad.
5. `RatingBarView` - Rating of the app in [0-5] range.
6. `NativeMediaView` - Media content of the native ad.
7. `CallToActionView` - Button for click.
8. `AdChoiceView` - Special ad icon provided by ad network.
**Templates implementation**:
To display them, all you need to do is:
1. Create programmatically or in your layout file one of View template classes
**Native template views classes:**
- `NativeAdViewNewsFeed`
- `NativeAdViewAppWall`
- `NativeAdViewContentStream`
**NativeAdView for custom implementation**:
To display it, all you need to do is:
1. Create a `NativeAdVIew` class programmatically or in your layout file
2. Inside the created `NativeAdView`, arrange all the `View`/`IconView`/`MediaView` you need for
displaying it in any style you prefer
3. Bind programmatically or in your layout file all necessary `View`/`IconView`/`MediaView`.
**Native view for a Custom Implementation**:
- `NativeAdView`
:::
You can use our **demo app** as a reference project.
Native Demo
## Integration guide
1. Create programmatically or in your layout file one of View template classes:
```xml showLineNumbers
```
```kotlin showLineNumbers
val newsFeedView = NativeAdViewNewsFeed(context)
val appWallView = NativeAdViewAppWall(context)
val contentStreamView = NativeAdViewContentStream(context)
```
```java showLineNumbers
NativeAdViewNewsFeed newsFeedView = new NativeAdViewNewsFeed(context);
NativeAdViewAppWall appWallView = new NativeAdViewAppWall(context);
NativeAdViewContentStream contentStreamView = new NativeAdViewContentStream(context);
```
2. Get a view instance from layout **OR** add a programmatically created ViewTemplate to your View
hierarchy:
```koltin showLineNumbers
val newsFeedView = findViewById(R.id.native_news_feed);
val appWallView = findViewById(R.id.native_app_wall);
val contentStreamView = findViewById(R.id.native_content_stream);
rootView.addView(newsFeedView)
rootView.addView(appWallView)
rootView.addView(contentStreamView)
```
```java showLineNumbers
NativeAdViewNewsFeed newsFeedView = findViewById(R.id.native_news_feed);
NativeAdViewAppWall appWallView = findViewById(R.id.native_app_wall);
NativeAdViewContentStream contentStreamView = findViewById(R.id.native_content_stream);
rootView.addView(newsFeedView);
rootView.addView(appWallView);
rootView.addView(contentStreamView);
```
3. When the NativeAd is loaded just register it
```kotlin showLineNumbers
if (Appodeal.isLoaded(Appodeal.NATIVE)) {
newsFeedView.registerView(Appodeal.getNativeAdCount(1))
}
```
```java showLineNumbers
if (Appodeal.isLoaded(Appodeal.NATIVE)) {
newsFeedView.registerView(Appodeal.getNativeAdCount(1));
}
```
```kotlin showLineNumbers
val needToShow = 3
if (Appodeal.getAvailableNativeAdsCount() >= needToShow) {
val nativeAds = Appodeal.getNativeAdCount(needToShow)
newsFeedView.registerView(nativeAds[0])
appWallView.registerView(nativeAds[1])
contentStreamView.registerView(nativeAds[2])
}
```
```java showLineNumbers
int needToShow = 3;
if (Appodeal.getAvailableNativeAdsCount() >= needToShow) {
List nativeAds = Appodeal.getNativeAdCount(needToShow);
newsFeedView.registerView(nativeAds.get(0));
appWallView.registerView(nativeAds.get(1));
contentStreamView.registerView(nativeAds.get(2);
}
```
4. When the display has been terminated and you no longer plan to use the NativeAdView, you should
call the destroy method:
```kotlin showLineNumbers
nativeAdView.destroy()
```
```java showLineNumbers
nativeAdView.destroy();
```
**General requirements:**
- `NativeAdView` must have min height as 32dp;
- `NativeMediaView` must have mim size as 120dp x 120dp;
- The `AdAttributionView` must clearly mark your nativeAd as "Ad" so that users don't mistake them for content;
- You are allowed to scale the `NativeIconView` or `NativeMediaView` down without modifying the aspect ratio;
- You are allowed to crop the `NativeIconView` or `NativeMediaView` symmetrically by up to 20% in only one dimension (height or width).
1. Create your markdown with NativeAdView as root:
:::tip
You can build a layout with any style, arrangement of elements and with any type of
`ViewGroup`(`ConstrainLayout`, `RelativeLayout`, `FrameLayout`)
:::
```jsx showLineNumbers
```
**Requirements for `NativeAdView` elements**
| Name of view | Type | Mandatory | Description |
|---------------------|-----------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `titleView` | TextView | Mandatory | Title of the native ad. Maximum 25 symbols of the title should always be displayed. You can add ellipsis at the end if the title is longer. |
| `callToActionView` | Button | Mandatory | Call-to-action button. Should be displayed without truncation on a visible button. |
| `descriptionView` | TextView | Optional | Text description of the native ad. If you choose to display the description, you should display maximum 100 characters. You can add ellipsis at the end. |
| `ratingView` | RatingBar | Optional | Rating of the app in [0-5] range |
| `adAttributionView` | TextView | Mandatory | Advertising Indicator. This is a TextView labeled "Ad". You can specify the color of the text and background. See [adAttribution settings](#adAttributionView-settings). You can place it anywhere inside the `NativeAdView`. |
| `adChoicesView` | ViewGroup | Attach automatically | Special ad icon provided by ad network. If SDK received AdChoice from ad network, it attached it automatically. You can specify a position in one of the corners of the `NativeAdView`. [See setAdChoice](#set-adChoice-position) position |
| `iconView` | NativeIconView | Mandatory/Optional | Media content of the native ad. |
| `mediaView` | NativeMediaView | Mandatory/Optional | Icon of the native ad. |
:::note
`NativeAdView` must contain either `NativeIconView` or `NativeMediaView`.
The `titleView`, `callToActionView` and `adAttributionView` has to be added in any cases.
:::
2. Set ids of all child views of `NativeAdView`.
:::tip
You can do this either in the xml file of markdown (recommended way) or programmatically
:::
```xml showLineNumbers
app:adAttributionViewId="@id/adAttribution">
```
```kotlin showLineNumbers
val nativeAdView: NativeAdView = ...
nativeAdView.titleViewId = R.id.titleView
nativeAdView.callToActionViewId = R.id.callToActionView
nativeAdView.descriptionViewId = R.id.descriptionView
nativeAdView.ratingViewId = R.id.ratingView
nativeAdView.iconViewId = R.id.iconView
nativeAdView.mediaViewId = R.id.mediaView
nativeAdView.adAttributionViewId = R.id.adAttribution
// OR
nativeAdView.titleView = findViewById(R.id.titleView)
nativeAdView.callToActionView = findViewById(R.id.callToActionView)
nativeAdView.descriptionView = findViewById(R.id.descriptionView)
nativeAdView.ratingView = findViewById(R.id.ratingView)
nativeAdView.iconView = findViewById(R.id.iconView)
nativeAdView.mediaView = findViewById(R.id.mediaView)
nativeAdView.adAttributionView = findViewById(R.id.adAttribution)
```
```java showLineNumbers
NativeAdView nativeAdView: NativeAdView = ...
nativeAdView.setTitleViewId(R.id.titleView);
nativeAdView.setCallToActionViewId(R.id.callToActionView);
nativeAdView.setDescriptionViewId(R.id.descriptionView);
nativeAdView.setRatingViewId(R.id.ratingView);
nativeAdView.setIconViewId(R.id.iconView);
nativeAdView.setMediaViewId(R.id.mediaView);
nativeAdView.setAdAttributionViewId(R.id.adAttribution);
// OR
nativeAdView.setTitleView(findViewById(R.id.titleView));
nativeAdView.setCallToActionView(findViewById(R.id.callToActionView));
nativeAdView.setDescriptionView(findViewById(R.id.descriptionView));
nativeAdView.setRatingView(findViewById(R.id.ratingView));
nativeAdView.setIconView(findViewById(R.id.iconView));
nativeAdView.setMediaView(findViewById(R.id.mediaView));
nativeAdView.setAdAttributionView(findViewById(R.id.adAttribution));
```
3. When the `NativeAd` is loaded just register it for showing:
```kotlin showLineNumbers
if (Appodeal.isLoaded(Appodeal.NATIVE)) {
newsFeedView.registerView(Appodeal.getNativeAdCount(1))
}
```
```java showLineNumbers
if (Appodeal.isLoaded(Appodeal.NATIVE)) {
newsFeedView.registerView(Appodeal.getNativeAdCount(1));
}
```
```kotlin showLineNumbers
val needToShow = 3
if (Appodeal.getAvailableNativeAdsCount() >= needToShow) {
val nativeAds = Appodeal.getNativeAdCount(needToShow)
nativeAdView1.registerView(nativeAds[0])
nativeAdView2.registerView(nativeAds[1])
nativeAdView3.registerView(nativeAds[2])
}
```
```java showLineNumbers
int needToShow = 3;
if (Appodeal.getAvailableNativeAdsCount() >= needToShow) {
List nativeAds = Appodeal.getNativeAdCount(needToShow);
nativeAdView1.registerView(nativeAds.get(0));
nativeAdView2.registerView(nativeAds.get(1));
nativeAdView3.registerView(nativeAds.get(2));
}
```
4. When the display has been terminated and you no longer plan to use the `NativeAdView`, you should
call the `destroy` method:
```kotlin showLineNumbers
nativeAdView.destroy()
```
```java showLineNumbers
nativeAdView.destroy();
```
:::tip
If you want to show a new NativeAd inside used nativeAdView, just call
`nativeAdView.registerView(newNativeAd)` method.
:::
## Check If Ad Is Loaded
To check if at least 1 instance of `NativeAd` is loaded, use the method:
```kotlin showLineNumbers
Appodeal.isLoaded(Appodeal.NATIVE)
```
```java showLineNumbers
Appodeal.isLoaded(Appodeal.NATIVE);
```
To get how many `NativeAd` instances are loaded, use the method:
```kotlin showLineNumbers
val nativeAmount = Appodeal.getAvailableNativeAdsCount()
```
```java showLineNumbers
int nativeAmount = Appodeal.getAvailableNativeAdsCount();
```
:::note
By default, the Appodeal SDK with AutoCahce enabled loads 2 instances of `NativeAd` each
:::
:::tip
We recommend you always check whether an ad is available before trying to show it.
:::
## Get Loaded Native Ads
To get loaded native ads, use the following method:
```kotlin showLineNumbers
val nativeAds: List = Appodeal.getNativeAds(amount)
```
```java showLineNumbers
List nativeAds = Appodeal.getNativeAds(int amount);
```
:::danger
Once you get the ads, they are removed from our SDK cache.
:::
## Display
To display `NativeAd`, you need to call the following code:
```kotlin showLineNumbers
NativeAdView.registerView(nativeAd: NativeAd)
```
```java showLineNumbers
NativeAdView.registerView(NativeAd nativeAd);
```
:::danger
SDK can't show ads without a network connection!
:::
`NativeAdView.registerView()` returns a **boolean** value indicating whether the show
method call was passed to the appropriate SDK.
:::info
Before the `registerView(nativeAd)` method is called, `NativeAdView` is in the `visibility == GONE`
state. After the call, the state will automatically change to `visibility == VISIBLE`.
You don't need to change the visibility state, Appodeal SDK does it automatically.
After calling `destroy()`, the state will automatically change to `visibility == GONE`.
:::
:::tip
`NativeAdView` and its successors have a built-in attribute `tools:visibility="visible"` so the view
will be displayed in your IDE markup during development.
:::
## Placements
Appodeal SDK allows you to tag each impression with different placement.
To use placements, you need to create placements in Appodeal Dashboard.
[Read more about placements](/advanced/placements).
To show an ad with placement, you have to call show method:
```kotlin showLineNumbers
NativeAdView.registerView(nativeAd: NativeAd, yourPlacementName: String)
```
```java showLineNumbers
NativeAdView.registerView(NativeAd nativeAd, String yourPlacementName)
```
:::info
If the loaded ad can’t be shown for a specific placement, nothing will be shown.
:::
If auto caching is enabled, sdk will start to cache another ad, which can affect display rate.
To save the loaded ad for future use (for instance, for another placement) check if the ad can be
shown before calling the show method:
```kotlin showLineNumbers
if (NativeAd.canShow(context: Context, yourPlacementName: String)) {
NativeAdView.registerView(nativeAd: NativeAd, yourPlacementName: String)
}
```
```java showLineNumbers
if (NativeAd.canShow(Context context, String yourPlacementName)) {
NativeAdView.registerView(NativeAd nativeAd, String yourPlacementName)
}
```
You can configure your impression logic for each placement.
:::info
If you have no placements, or call `NativeAdView.registerView` with a placement that does not exist,
the impression will be tagged with 'default' placement and its settings will be applied.
:::
:::note
Placement settings affect ONLY ad presentation, not loading or caching.
:::
## UnregisterView
To unregister the view from displaying the currently registered native ad use the method:
```kotlin showLineNumbers
NativeAdView.unregisterView()
```
```java showLineNumbers
NativeAdView.unregisterView();
```
:::note
`UnregisterView` method **does not hide** the `NativeAdView`. It suspends the NativeAd display
tracking.
:::
:::tip
`UnregisterView` makes sense to use, for example, if the NativeAdView is out of the screen while
scrolling in the list, or is temporarily overlapped by another `View`/`Fragment`/`Activity`
:::
## Destroy
To destroy the native ad view and perform any necessary cleanup, and hide `NativeAdView` use the
method:
```kotlin showLineNumbers
NativeAdView.destroy()
```
```java showLineNumbers
NativeAdView.destroy();
```
:::info
This method should be called when the native ad is no longer needed.
Also, when `destroy()` is called, the `unregisterView` logic is triggered.
:::
## Callbacks
```kotlin showLineNumbers
Appodeal.setNativeCallbacks(object : NativeCallbacks {
override fun onNativeLoaded() {
// Called when native ads are loaded
}
override fun onNativeFailedToLoad() {
// Called when native ads are failed to load
}
override fun onNativeShown(nativeAd: NativeAd) {
// Called when native ad is shown
}
override fun onNativeShowFailed(nativeAd: NativeAd) {
// Called when native ad show failed
}
override fun onNativeClicked(nativeAd: NativeAd) {
// Called when native ads is clicked
}
override fun onNativeExpired() {
// Called when native ads is expired
}
})
```
```java showLineNumbers
Appodeal.setNativeCallbacks(new NativeCallbacks() {
@Override
public void onNativeLoaded() {
// Called when native ads are loaded
}
@Override
public void onNativeFailedToLoad() {
// Called when native ads are failed to load
}
@Override
public void onNativeShown(NativeAd nativeAd) {
// Called when native ad is shown
}
@Override
public void onNativeShowFailed(NativeAd nativeAd) {
// Called when native ad show failed
}
@Override
public void onNativeClicked(NativeAd nativeAd) {
// Called when native ads is clicked
}
@Override
public void onNativeExpired() {
// Called when native ads is expired
}
});
```
:::info
All callbacks are called on the main thread.
:::
## Cache Manually
To disable automatic caching for native ads, use the code below before the SDK initialization:
```kotlin showLineNumbers
Appodeal.setAutoCache(Appodeal.NATIVE, false)
```
```java showLineNumbers
Appodeal.setAutoCache(Appodeal.NATIVE, false);
```
Read more on manual caching in our [**
FAQ**](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
## Cache
To cache native ads, use:
```kotlin showLineNumbers
Appodeal.cache(this, Appodeal.NATIVE)
```
```java showLineNumbers
Appodeal.cache(this, Appodeal.NATIVE);
```
To cache multiple native ads, use:
```kotlin showLineNumbers
Appodeal.cache(this, Appodeal.NATIVE, 3)
```
```java showLineNumbers
Appodeal.cache(this, Appodeal.NATIVE, 3);
```
:::note
You may request a **maximum of 5** `NativeAd`
The number of cached ads is not guaranteed and could be less than requested.
:::
## Check If Ad Is Initialized
To check if `NativeAd` was initialized, you can use the method:
```kotlin showLineNumbers
Appodeal.isInitialized(Appodeal.NATIVE)
```
```java showLineNumbers
Appodeal.isInitialized(Appodeal.NATIVE);
```
Returns`true`, if the `NativeAd` was initialized.
## Check If Autocache Is Enabled
To check if autocache is enabled for `NativeAd`, you can use the
method:
```kotlin showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.NATIVE)
```
```java showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.NATIVE);
```
Returns `true`, if autocache is enabled for native.
## Get Predicted eCPM
To get the predicted eCPM from the next block in the caching queue, use the method:
```kotlin showLineNumbers
NativeAd.predictedEcpm
```
```java showLineNumbers
NativeAd.getPredictedEcpm();
```
# Configuration
## Set preferred media content type
You can tell the Appodeal SDK your preferred content type for NativeAd.
To do this, use the method:
```kotlin showLineNumbers
// both static image and video native ads will be loaded
Appodeal.setPreferredNativeContentType(NativeMediaViewContentType.Auto)
// only static image native ads will be loaded
Appodeal.setPreferredNativeContentType(NativeMediaViewContentType.NoVideo)
// only video native ads will be loaded.
Appodeal.setPreferredNativeContentType(NativeMediaViewContentType.Video)
```
```java showLineNumbers
// both static image and video native ads will be loaded
Appodeal.setPreferredNativeContentType(NativeMediaViewContentType.Auto);
// only static image native ads will be loaded
Appodeal.setPreferredNativeContentType(NativeMediaViewContentType.NoVideo);
// only video native ads will be loaded.
Appodeal.setPreferredNativeContentType(NativeMediaViewContentType.Video);
```
:::info
Setting a video type does not guarantee that it will be loaded, but only indicates the preferred
type.
:::
To check if the downloaded advertisement contains video you can use the method:
```kotlin showLineNumbers
NativeAd.containsVideo()
```
```java showLineNumbers
NativeAd.containsVideo();
```
Return `true` if `NativeAd` contains video
Use the method to retrieve the preferred content type:
```kotlin showLineNumbers
Appodeal.getPreferredNativeContentType()
```
```java showLineNumbers
Appodeal.getPreferredNativeContentType();
```
:::note
Only affects content inside the `NativeMediaView`. Therefore, it makes sense to use it only in
case of `NativeAdViewContentStream` template or your out custom implementation of `NativeAdView`.
Content for `NativeIconView` is always a static image
:::
## Set adChoice position
You can specify a position in one of the corners of the `NativeAdView`:
```xml showLineNumbers
app:adChoicePosition="end_top"
```
```kotlin showLineNumbers
nativeAdView.setAdChoicesPosition(Position.END_TOP)
```
```java showLineNumbers
nativeAdView.setAdChoicesPosition(Position.END_TOP);
```
:::info
As a `Position` you can specify one of 4 options:
`START_TOP` - matches to the upper left corner of `NativeAdView`;
`START_BOTTOM` - matches to the lower left corner of `NativeAdView`;
`END_TOP` - matches the upper right corner of `NativeAdView`;
`END_BOTTOM` - matches the bottom right corner of `NativeAdView`.
:::
## AdAttributionView settings
You may set text color and background color for AdAttributionView in `NativeAdView`:
```xml showLineNumbers
app:adAttributionBackgroundColor="@color/red"
app:adAttributionTextColor="@color/black"
```
```kotlin showLineNumbers
nativeAdView.setAdAttributionBackground(Color.RED)
nativeAdView.setAdAttributionTextColor(Color.BLACK)
```
```java showLineNumbers
nativeAdView.setAdAttributionBackground(Color.RED);
nativeAdView.setAdAttributionTextColor(Color.BLACK);
```
:::info
Color should have ColorInt format. See [`android.graphics.Color`](https://developer.android.com/reference/android/graphics/Color).
:::
:::note
For custom `NativeAdView`, you may do the same via your xml markup using attributes `android:textColor`
and `android:background` for adAttribution `TextView`.
:::
## Works with lists
To use `NativeAd` in `RecyclerView`, you can use the following example:
1. Create an ListItem entity that will serve to define the `itemViewType` in RecyclerView.ListAdapter:
```kotlin showLineNumbers
sealed interface ListItem {
fun getItemId(): Int
class NativeAdItem(val getNativeAd: () -> NativeAd?) : ListItem {
override fun getItemId() = NATIVE_AD_ITEM
companion object {
const val NATIVE_AD_ITEM = 3
}
}
data class YourDataItem(val userData: Int) : ListItem {
override fun getItemId() = USER_ITEM
companion object {
const val USER_ITEM = 2
}
}
}
```
```java showLineNumbers
public interface ListItem {
int getItemId();
int hashCode();
}
public final class NativeAdItem implements ListItem {
private final NativeAd nativeAd;
public NativeAdItem(NativeAd nativeAd) {
this.nativeAd = nativeAd;
}
@Override
public int getItemId() {
return NATIVE_AD_ITEM;
}
public NativeAd getNativeAd() {
return nativeAd;
}
public static final int NATIVE_AD_ITEM = 3;
}
public final class YourDataItem implements ListItem {
private final int userData;
public YourDataItem(int userData) {
this.userData = userData;
}
@Override
public int getItemId() {
return USER_ITEM;
}
public int getUserData() {
return userData;
}
public static final int USER_ITEM = 2;
}
```
2. Create a `DiffUtil.ItemCallback` entity that will show the `ListAdapter` the differences
between items:
```kotlin showLineNumbers
internal class DiffUtils : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: ListItem, newItem: ListItem) =
oldItem.getItemId() == newItem.getItemId()
override fun areContentsTheSame(oldItem: ListItem, newItem: ListItem) =
oldItem.hashCode() == newItem.hashCode()
}
```
```java showLineNumbers
class DiffUtils extends DiffUtil.ItemCallback {
@Override
public boolean areItemsTheSame(ListItem oldItem, ListItem newItem) {
return oldItem.getItemId() == newItem.getItemId();
}
@Override
public boolean areContentsTheSame(ListItem oldItem, ListItem newItem) {
return oldItem.hashCode() == newItem.hashCode();
}
}
```
3. Create a `ListAdapter` entity, which will be an adapter for `RecyclerView`:
```kotlin showLineNumbers
class NativeListAdapter : ListAdapter(DiffUtils()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListHolder {
return when (viewType) {
NATIVE_AD_ITEM -> { DynamicAdViewHolder(NativeAdViewContentStream(parent.context)) }
else -> {
YourViewHolder(
YourDataItemBinding.inflate(LayoutInflater.from(parent.context),
parent,
false
))
}
}
}
override fun onBindViewHolder(holder: ListHolder, position: Int) {
when (val item = getItem(position)) {
is ListItem.YourDataItem -> (holder as YourViewHolder).bind(item)
is ListItem.NativeAdItem -> (holder as DynamicAdViewHolder).bind(item)
}
}
override fun getItemViewType(position: Int): Int {
return when (currentList[position]) {
is ListItem.YourDataItem -> USER_ITEM
is ListItem.NativeAdItem -> NATIVE_AD_ITEM
}
}
sealed class ListHolder(root: View) : RecyclerView.ViewHolder(root) {
class YourViewHolder(private val binding: YourDataItemBinding) : ListHolder(binding.root) {
fun bind(item: ListItem.YourDataItem) {
binding.root.text = item.userData.toString()
}
}
class DynamicAdViewHolder(itemView: View) : ListHolder(itemView) {
fun bind(item: ListItem.NativeAdItem) {
val nativeAd = item.getNativeAd.invoke()
if (nativeAd != null) {
(itemView as NativeAdView).registerView(nativeAd)
}
}
}
}
}
```
```java showLineNumbers
public class NativeListAdapter extends ListAdapter {
public NativeListAdapter() {
super(new DiffUtils());
}
@Override
public ListHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == NATIVE_AD_ITEM) {
return new DynamicAdViewHolder(new NativeAdViewContentStream(parent.getContext()));
} else {
YourDataItemBinding binding = YourDataItemBinding.inflate(inflater,
parent,
false);
return new YourViewHolder(binding);
}
}
@Override
public void onBindViewHolder(ListHolder holder, int position) {
ListItem item = getItem(position);
if (item instanceof YourDataItem) {
((YourViewHolder) holder).bind((YourDataItem) item);
} else if (item instanceof NativeAdItem) {
((DynamicAdViewHolder) holder).bind((NativeAdItem) item);
}
}
@Override
public int getItemViewType(int position) {
ListItem item = getItem(position);
if (item instanceof YourDataItem) {
return USER_ITEM;
} else if (item instanceof NativeAdItem) {
return NATIVE_AD_ITEM;
}
return super.getItemViewType(position);
}
abstract static class ListHolder extends RecyclerView.ViewHolder {
ListHolder(View root) {
super(root);
}
}
static class YourViewHolder extends ListHolder {
private YourDataItemBinding binding;
YourViewHolder(YourDataItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void bind(YourDataItem item) {
binding.getRoot().setText(String.valueOf(item.getUserData()));
}
}
static class DynamicAdViewHolder extends ListHolder {
DynamicAdViewHolder(View itemView) {
super(itemView);
}
void bind(NativeAdItem item) {
NativeAd nativeAd = item.getNativeAd();
if (nativeAd != null) {
((NativeAdView) itemView).registerView(nativeAd);
}
}
}
}
```
:::info
`NATIVE_AD_ITEM` - `NativeAdItem.NATIVE_AD_ITEM`
:::
4. As the markup of your Activity/Fragment, we use the markup
```jsx title=activity_main.xml showLineNumbers
```
We'll use the example as the markup of your YourDataItem:
```jsx title=yout_data_item.xml showLineNumbers
```
5. In your `Activity`/`Fragment`, add the following code
```kotlin showLineNumbers
class NativeActivity : AppCompatActivity() {
private val getNativeAd: () -> NativeAd? = { Appodeal.getNativeAds(1).firstOrNull() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityNativeBinding.inflate(layoutInflater)
setContentView(binding.root)
val nativeListAdapter = NativeListAdapter()
binding.recyclerView.adapter = nativeListAdapter
setUpAppodealSDK()
}
private fun setUpAppodealSDK() {
Appodeal.setLogLevel(LogLevel.verbose)
Appodeal.setTesting(true)
Appodeal.initialize(this, APPODEAL_APP_KEY, Appodeal.NATIVE) { errors ->
val initResult = if (errors.isNullOrEmpty()) "successfully" else "with ${errors.size} errors"
Log.d("TAG", "onInitializationFinished: $initResult")
}
}
private fun obtainData(nativeListAdapter: NativeListAdapter) {
val yourDataItems = generateYourData()
nativeListAdapter.submitList(yourDataItems.addNativeAdItems())
}
private fun List.addNativeAdItems() =
this.foldIndexed(
initial = listOf(),
operation = { index: Int, acc: List, yourDataItem: ListItem ->
val shouldAdd = index % STEPS == 0 && index != 0
if (shouldAdd) {
acc + createNativeAdItem() + yourDataItem
} else {
acc + yourDataItem
}
}
)
private fun generateYourData(): List =
(1..USER_DATA_SIZE).toList().map { ListItem.YourDataItem(userData = it) }
private fun createNativeAdItem(): ListItem.NativeAdItem =
ListItem.NativeAdItem(getNativeAd = getNativeAd)
}
private const val USER_DATA_SIZE = 200
private const val STEPS = 5
```
```java showLineNumbers
public class NativeActivity extends AppCompatActivity {
private static final String TAG = "NativeActivity";
private static final String APP_KEY = "YOUR_APP_KEY";
private static final int USER_DATA_SIZE = 200;
private static final int STEPS = 5;
private final GetNativeAdCallback getNativeAd = () -> {
List nativeAds = Appodeal.getNativeAds(1);
return nativeAds.size() > 0 ? nativeAds.get(0) : null;
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityNativeBinding binding = ActivityNativeBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
NativeListAdapter nativeListAdapter = new NativeListAdapter();
binding.recyclerView.setAdapter(nativeListAdapter);
setUpAppodealSDK();
}
private void setUpAppodealSDK() {
Appodeal.setTesting(true);
Appodeal.initialize(this, APPODEAL_APP_KEY, Appodeal.NATIVE, errors -> {
if (errors == null || errors.isEmpty()) {
Log.d(TAG, "onInitializationFinished: successfully");
} else {
Log.d(TAG, "onInitializationFinished: with " + errors.size() + " errors");
}
});
}
private void obtainData(NativeListAdapter nativeListAdapter) {
List yourDataItems = generateYourData();
List itemsWithNativeAd = addNativeAdItems(yourDataItems);
nativeListAdapter.submitList(itemsWithNativeAd);
}
private List generateYourData() {
List yourDataItems = new ArrayList<>();
for (int i = 1; i <= USER_DATA_SIZE; i++) {
yourDataItems.add(new YourDataItem(i));
}
return yourDataItems;
}
private List addNativeAdItems(List yourDataItems) {
List itemsWithNativeAd = new ArrayList<>();
for (int i = 0; i < yourDataItems.size(); i++) {
ListItem yourDataItem = yourDataItems.get(i);
boolean shouldAdd = i % STEPS == 0 && i != 0;
if (shouldAdd) {
itemsWithNativeAd.add(createNativeAdItem());
}
itemsWithNativeAd.add(yourDataItem);
}
return itemsWithNativeAd;
}
private NativeAdItem createNativeAdItem() {
return new NativeAdItem(getNativeAd.getNativeAd());
}
interface GetNativeAdCallback {
NativeAd getNativeAd();
}
}
```
:::info
`STEPS` - step through which the insertion of `NativeAd` will be repeated;
`addNativeAdItems()` - logic of inserting `NativeAd` into the list through a certain number of `STEPS`.
:::
Done! When you want to insert native ads into `RecyclerView`, simply call the `obtainData()` method
## Common Mistakes
- **No adAttributionView**
The majority of ad networks require publishers to add a special mark to a native ad, so users don’t
mistake them for content. That’s why you always need to make sure, that native ads in your app have
the ad attribution (e.g., “Ad”) or the AdChoices icon.
- **Absence of the required native ad elements**
Every native ad should contain:
- titleView TextView;
- callToActionView Button;
- adAttribution TextView;
- `NativeIconView` or `NativeMedaiaView`.
- **Native ad elements alteration**
Advertisers expect that their ads will be displayed clearly and without any alteration. You can
scale buttons and images, but you shouldn't crop, cover or distort them.
- **Overlaying elements of native ads on each other**
Make sure, that all elements of a native ad are visible and not overlaid.
Native ads requirements:
- All of the fields of native ad marked as mandatory must be displayed.
- Image assets can be resized to fit your ad space but should not be significantly distorted or
cropped.
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the **Native \[Notify Shown\]** log if show was tracked
successfully.
```kotlin showLineNumbers
Appodeal com.example.app D Native [Notify Shown]
```
---
## Rewarded video
Rewarded videos are user-initiated ads where users can earn in-app rewards in exchange for viewing a video.
You can use our **demo rewarded video app** as a reference project.
## Check If Ad Is Loaded
You can check if the ad has been loaded before showing it. This method
returns a boolean value indicating whether or not the rewarded video has
been loaded.
```kotlin showLineNumbers
Appodeal.isLoaded(Appodeal.REWARDED_VIDEO)
```
```java showLineNumbers
Appodeal.isLoaded(Appodeal.REWARDED_VIDEO);
```
We recommend you always check whether an ad is available before trying to show it.
```kotlin showLineNumbers
if (Appodeal.isLoaded(Appodeal.REWARDED_VIDEO)) {
Appodeal.show(this, Appodeal.REWARDED_VIDEO)
}
```
```java showLineNumbers
if (Appodeal.isLoaded(Appodeal.REWARDED_VIDEO)) {
Appodeal.show(this, Appodeal.REWARDED_VIDEO);
}
```
## Display
To display rewarded video, you need to call the following code in the activity:
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.REWARDED_VIDEO)
```
```java showLineNumbers
Appodeal.show(this, Appodeal.REWARDED_VIDEO);
```
:::caution SDK can't show ads without a network connection!
:::
The method returns a **boolean** value indicating whether the call to the show method was passed to the appropriate SDK.
## Manual Caching
By default, auto caching is enabled: Appodeal SDK starts to load rewarded videos right after the initialization method is called.
The next rewarded videos ad starts to load after the previous one has been closed.
To disable automatic caching for rewarded videos, use the code below before SDK initialization:
```kotlin showLineNumbers
Appodeal.setAutoCache(Appodeal.REWARDED_VIDEO, false)
```
```java showLineNumbers
Appodeal.setAutoCache(Appodeal.REWARDED_VIDEO, false);
```
To cache rewarded video use:
```kotlin showLineNumbers
Appodeal.cache(this, Appodeal.REWARDED_VIDEO)
```
```java showLineNumbers
Appodeal.cache(this, Appodeal.REWARDED_VIDEO);
```
Read more on manual caching in our [**FAQ**](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
## Callbacks
```kotlin showLineNumbers
Appodeal.setRewardedVideoCallbacks(object : RewardedVideoCallbacks {
override fun onRewardedVideoLoaded(isPrecache: Boolean) {
// Called when rewarded video is loaded
}
override fun onRewardedVideoFailedToLoad() {
// Called when rewarded video failed to load
}
override fun onRewardedVideoShown() {
// Called when rewarded video is shown
}
override fun onRewardedVideoShowFailed() {
// Called when rewarded video show failed
}
override fun onRewardedVideoClicked() {
// Called when rewarded video is clicked
}
override fun onRewardedVideoFinished(amount: Double, currency: String) {
// Called when rewarded video is viewed until the end
}
override fun onRewardedVideoClosed(finished: Boolean) {
// Called when rewarded video is closed
}
override fun onRewardedVideoExpired() {
// Called when rewarded video is expired
}
})
```
```java showLineNumbers
Appodeal.setRewardedVideoCallbacks(new RewardedVideoCallbacks() {
@Override
public void onRewardedVideoLoaded(boolean isPrecache) {
// Called when rewarded video is loaded
}
@Override
public void onRewardedVideoFailedToLoad() {
// Called when rewarded video failed to load
}
@Override
public void onRewardedVideoShown() {
// Called when rewarded video is shown
}
@Override
public void onRewardedVideoShowFailed() {
// Called when rewarded video show failed
}
@Override
public void onRewardedVideoClicked() {
// Called when rewarded video is clicked
}
@Override
public void onRewardedVideoFinished(double amount, String name) {
// Called when rewarded video is viewed until the end
}
@Override
public void onRewardedVideoClosed(boolean finished) {
// Called when rewarded video is closed
}
@Override
public void onRewardedVideoExpired() {
// Called when rewarded video is expired
}
});
```
:::info
All callbacks are called on the main thread.
:::
## Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```kotlin showLineNumbers
Appodeal.show(this, Appodeal.REWARDED_VIDEO, "yourPlacementName")
```
```java showLineNumbers
Appodeal.show(this, Appodeal.REWARDED_VIDEO, "yourPlacementName");
```
If the loaded ad can't be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement), check if the ad can be shown
before calling show method:
```kotlin showLineNumbers
if (Appodeal.canShow(Appodeal.REWARDED_VIDEO, "yourPlacementName")) {
Appodeal.show(this, Appodeal.REWARDED_VIDEO, "yourPlacementName")
}
```
```java showLineNumbers
if (Appodeal.canShow(Appodeal.REWARDED_VIDEO, "yourPlacementName")) {
Appodeal.show(this, Appodeal.REWARDED_VIDEO, "yourPlacementName");
}
```
You can configure your impression logic for each placement.
If you have no placements or call `Appodeal.show` with a placement that
does not exist, the impression will be tagged with `default` placement
with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
## Server-to-Server Callbacks
To secure your apps economy we offer S2S reward callbacks. To validate each reward, you need to set up a callback URL
on your server that will receive the reward information. We will pass the user data to your callback URL,
which you will need to validate and adjust the user balance accordingly.
1. Create the reward callback URL on your server that will receive the reward information.
2. Fill the created URL and the encryption key in the app settings in your dashboard.
3. The reward callback will be sent to your URL using GET request with two parameters:
```as3
{http:/example.com/reward}?data1={data1}&data2={data2}
```
4. Your URL should decrypt the data and validate it.
5. Check `impression_id` for uniqueness and store it in your system to prevent duplicate transactions.
To set user ID, use the `Appodeal.setUserID("User#123")` method before SDK initialization.
We offer sample scripts in Go, PHP, Ruby, Java, Node.js, Python 3 and C# to decrypt the data.
If you need samples in other languages, please contact our support team and we will provide them to you.
- Sample in PHP: [reward.php](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.php).
- Sample in Ruby: [reward.rb](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.rb).
- Sample in Java: [reward.java](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/Reward.java).
- Sample in Node.js: [reward.js](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.js) .
- Sample in Python 3: [reward.py](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.py).
- Sample in C#: [reward.cs](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/Reward.cs).
- Sample in Go: [reward.go](https://appodeal-android.s3-us-west-1.amazonaws.com/reward/reward.go).
## Getting Reward Data For A Specific Placement
To get the reward data set for a specific placement use the following method before showing the
rewarded video:
```kotlin showLineNumbers
val reward: Reward = Appodeal.getReward("yourPlacementName")
val amount: Double = reward.amount
val currency: String? = reward.currency
```
```java showLineNumbers
Reward reward = Appodeal.getReward("yourPlacementName");
double amount = reward.getAmount();
String currency = reward.getCurrency();
```
## Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```kotlin showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.REWARDED_VIDEO)
```
```java showLineNumbers
Appodeal.getPredictedEcpm(Appodeal.REWARDED_VIDEO);
```
:::tip This method is reasonable to use if manual caching of ads is enabled.
:::
## Check If Ad Is Initialized
To check if rewarded video was initialized, you can use the method:
```kotlin showLineNumbers
Appodeal.isInitialized(Appodeal.REWARDED_VIDEO)
```
```java showLineNumbers
Appodeal.isInitialized(Appodeal.REWARDED_VIDEO);
```
Returns `true`, if the rewarded video was initialized.
## Check If Autocache Is Enabled
To check if autocache is enabled for rewarded video, you can use the method:
```kotlin showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.REWARDED_VIDEO)
```
```java showLineNumbers
Appodeal.isAutoCacheEnabled(Appodeal.REWARDED_VIDEO);
```
Returns `true`, if autocache is enabled for rewarded video.
## Mute Videos If Call Volume Is Muted
You can mute the sound in a rewarded video using the method:
```kotlin showLineNumbers
Appodeal.muteVideosIfCallsMuted(true)
```
```java showLineNumbers
Appodeal.muteVideosIfCallsMuted(true);
```
:::note
This method works if the user's device has silent mode or only vibration enabled.
:::
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the **RewardedVideo \[Notify Shown\]** log if show was tracked successfully.
```log showLineNumbers
Appodeal com.example.app D RewardedVideo [Notify Shown]
```
---
## Adjust
The Appodeal SDK gives you tools to grow your mobile apps & games. Adjust is one of them.
Use the Adjust account to track your attribution & analytics metrics from your UA campaigns.
Evaluate your soft launch and other marketing campaigns from the Appodeal Reports page that you will
find inside your Appodeal Dashboard.
- Compare Ads vs. IAPs vs. subscription revenues;
- Get Forecasted LTV based on UA campaigns;
- Find out which Ad Creatives bring top-paying users;
- Sync your retention metrics with your ARPU & revenues;
- Build deep granular reports to find out new growth opportunities.
We have two options for linking Adjust:
- **Our Adjust account.**
:::info
There is a limit of 10 000 non-organic installs per month.
If you are planning to run UA campaigns in near future, you can link our Adjust account.
:::
- **Your own Adjust account.**
------------------------
## Integration Steps
To connect with Adjust, follow the steps:
Step 1. Import Adjust
Complete all the steps from our [**integration guide**](../get-started).
Make sure to integrate Adjust distributed via Appodeal SDK.
Step 2. Contact Us
Contact our support team via live chat or via email [support@appodeal.com](support@appodeal.com) with the following information:
- The desired option.
- Links to the apps in store, which you want to connect.
- Traffic sources, where you are planning to run UA campaigns.
Support team will finish your Adjust integration from Appodeal side and let you know.
Step 1. Import Adjust
Make sure you have all the following features on your account before the connection:
- CSV uploads (from Business plan).
- Cost reporting (from Custom plan).
- Kpi-service (from Custom plan).
Complete all the steps from our [**integration guide**](../get-started).
Make sure to integrate Adjust distributed via Appodeal SDK.
Step 2. Add Your Adjust Account To Appodeal
Add your Adjust account to Appodeal [here](https://app.appodeal.com/integrations/user_acquisition).
You will need to enter your Account name and User Token
from Adjust.User Token - your Api Token on Adjust side.You can check Raw Data export to Amazon s3
box if you have your AWS (if you don't have it leave this box unchecked).

Step 3. Add Your App On Adjust Side
1. Add the following information:
- app name
- platform (add your app bundle id)
- reporting currency (USD is preferable)
2. Create your app
3. Go to all Settings → S2S Security → Create token & Activate S2S Authentication
Save this S2S Security Token for the next step.
Step 4. Turn On Adjust In Attribution Settings
Go to your app settings in your Appodeal account and choose **Attribution Settings**.
**Primary MMP Account** - your MMP account from where we can get attribution data.
**Secondary MMP Account**(optional) - this option is needed if you transfer from one MMP account to another or if you want
to test two different MMP's .
**Raw Data Source** - the source of raw data.
For Primary MMP Account choose your Adjust account, you can leaveSecondary MMP Account empty, for Raw Data Source choose Amazon S3
Bucket (if you have one and if you have added it with your Adjust account) or choose Global
Callback(enabled by default).For Attribution Platform choose Adjust, Adjust S2S Security Token
(can be copied from Adjust AppSettings → S2S Security from the previous step )and Adjust App Token
is in your app settings, choose Production for Adjust Environment.
Step 5. Add Global Callback On Adjust Side
Go to your Adjust account app settings → Raw Data Export → Real-Time Callbacks → Add Global Callback
(the one copied in Step 4)
Step 6. Create required events on Adjust side
If you use your own Adjust account, you need to add required events according to
[**this guide**](../advanced/event-tracking) so that in-app purchases will work correctly.
Step 7. Set Up Traffic Sources On Adjust Side
In order to see data of your campaign, you need to set up your traffic sources on Adjust side such
as [**Meta**](https://help.adjust.com/en/article/skad-facebook-integration)
or [**Google**](https://help.adjust.com/en/article/skad-google-integration), for example.
You can use [**this preset**](https://app.appodeal.com/analytics/reports?q=(f~(INSTALL*_DATE~(from~*2022-01-19~to~*2022-01-25)ATTRIBUTION*_NETWORK*_HID~!*333266372425416704)g~!ATTRIBUTION*_AD*_SET*_HID~~m~!installs~(id~retention*_rate~d~1)(id~retention*_rate~d~3)avg*_full*_time*_per*_user*_per*_day~avg*_full*_session*_length~(id~cumulative*_ad*_arpu~d~0)(id~cumulative*_ad*_arpu~d~3)~view~table~fv~*)~&trace=aab07f79934bd99d)
to check the statistics of your UA campaign.
Step 8. Turn On Ad Spend Tracking
In order to be able to see Ad Spend data make sure to complete the steps from [**this guide**](https://help.adjust.com/en/article/ad-spend)
and link your traffic source account to Adjust.
------------
## Demo Application
You can use our **demo analytics app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## AppsFlyer
:::note Before the start
AppsFlyer is available for linking only with your own AppsFlyer account with Premium Plan.
Make sure you have the following features:
- [DataLocker](https://support.appsflyer.com/hc/en-us/articles/360000877538-Data-Locker-for-Advertisers).
Data Locker writes your report data to cloud storage for loading into your BI systems.
- [Master API](https://support.appsflyer.com/hc/en-us/articles/213223166-Using-Master-API-campaign-performance-KPIs).
Get selected LTV, activity, Protect360, and retention campaign performance KPIs by API, in CSV or JSON format.
Select 1 or more apps.
These features are available on **AppsFlyer Premium Plan**.
Contact our support team via live chat or via email [support@appodeal.com](mailto:support@appodeal.com) to enable
Attribution Settings needed in step 4, this feature is absolutely free.
:::
AppsFlyer is a mobile marketing, analytics, and attribution platform.
With one connection of AppsFlyer you will be able to see all UA metrics
directly in our BI, without using MMP, analyze them in various sections,
and also get access to LTV forecasting.
Note that we also support [forecast metrics](/reporting/revenue-forecast), which will be available by
default with the current integration.
------------------------
## Integration Steps
To connect with AppsFlyer, follow the steps:
Step 1. Import AppsFlyer
Complete all the steps from our [**integration guide**](../get-started). Make sure to integrate
AppsFlyer distributed via Appodeal SDK.
Step 2. Add Your AppsFlyer Account
Add your AppsFlyer account to Appodeal [**here**](https://app.appodeal.com/integrations/user_acquisition).
You will need to enter the following:
- Your Account name,
- Master API Token (can be found in your **AppsFlyer account** → **API tokens** [**here**](https://hq1.appsflyer.com/account/api-tokens)
- Data for Amazon s3 bucket, you can find it in [**Datalocker**](https://hq1.appsflyer.com/datalocker/overview)
Step 3. Set Up Datalocker
You need to set up Datalocker according to [**this guide**](https://support.appsflyer.com/hc/en-us/articles/360000877538?utm_source=hq1&utm_medium=referral#set-up-data-locker)
on the AppsFlyer side.
Make sure to indicate fields and report types.
Here is the required minimum for fields:
```text showLineNumbers
Advertising ID (advertising_id)
Ad (af_ad)
Ad ID (af_ad_id)
Ad Type (af_ad_type)
Adset Name (af_adset)
Adset ID (af_adset_id)
Attribution Lookback Window (af_attribution_lookback)
Campaign ID (af_c_id)
Channel (af_channel)
Cost Currency (af_cost_currency)
Cost Model (af_cost_model)
Cost Value (af_cost_value)
Keywords (af_keywords)
Partner (af_prt)
Reengagement Window (af_reengagement_window)
Site ID (af_siteid)
Sub Param 1 (af_sub1)
Sub Param 2 (af_sub2)
Sub Param 3 (af_sub3)
Sub Param 4 (af_sub4)
Sub Param 5 (af_sub5)
Sub Site ID (af_sub_siteid)
Web ID (af_web_id)
Amazon Fire ID (amazon_aid)
Android ID (android_id)
App ID (app_id)
App Name (app_name)
App Version (app_version)
AppsFlyer ID (appsflyer_id)
Attributed Touch Time (attributed_touch_time)
Attributed Touch Type (attributed_touch_type)
Blocked Reason (blocked_reason)
Blocked Reason Rule (blocked_reason_rule)
Blocked Reason Value (blocked_reason_value)
Blocked Sub Reason (blocked_sub_reason)
Bundle ID (bundle_id)
Campaign (campaign)
Carrier (carrier)
City (city)
Contributor1 Partner (contributor_1_af_prt)
Contributor1 Campaign (contributor_1_campaign)
Contributor1 Match Type (contributor_1_match_type)
Contributor1 Media Source (contributor_1_media_source)
Contributor1 Touch Time (contributor_1_touch_time)
Contributor1 Touch Type (contributor_1_touch_type)
Contributor2 Partner (contributor_2_af_prt)
Contributor2 Campaign (contributor_2_campaign)
Contributor2 Match Type (contributor_2_match_type)
Contributor2 Media Source (contributor_2_media_source)
Contributor2 Touch Time (contributor_2_touch_time)
Contributor2 Touch Type (contributor_2_touch_type)
Contributor3 Partner (contributor_3_af_prt)
Contributor3 Campaign (contributor_3_campaign)
Contributor3 Match Type (contributor_3_match_type)
Contributor3 Media Source (contributor_3_media_source)
Contributor3 Touch Time (contributor_3_touch_time)
Contributor3 Touch Type (contributor_3_touch_type)
Country Code (country_code)
Custom Data (custom_data)
Customer User ID (customer_user_id)
Deeplink URL (deeplink_url)
Device Category (device_category)
Device Download Time (device_download_time)
Device Type (device_type)
DMA (dma)
Event Name (event_name)
Event Revenue (event_revenue)
Event Revenue Currency (event_revenue_currency)
Event Revenue USD (event_revenue_usd)
Event Source (event_source)
Event Time (event_time)
Event Value (event_value)
Google Play Broadcast Referrer (gp_broadcast_referrer)
Google Play Click Time (gp_click_time)
Google Play Install Begin Time (gp_install_begin)
Google Play Referrer (gp_referrer)
HTTP Referrer (http_referrer)
IDFA (idfa)
IDFV (idfv)
IMEI (imei)
Adrevenue Impressions (impressions)
Install App Store (install_app_store)
Install Time (install_time)
IP (ip)
Is Primary Attribution (is_primary_attribution)
Is Receipt Validated (is_receipt_validated)
Is Retargeting (is_retargeting)
Keyword Match Type (keyword_match_type)
Language (language)
Match Type (match_type)
Media Source (media_source)
Adrevenue Mediation Network (mediation_network)
Adrevenue Network (monetization_network)
Network Account ID (network_account_id)
OAID (oaid)
Operator (operator)
Original URL (original_url)
OS Version (os_version)
Adrevenue Placement (placement)
Platform (platform)
Postal Code (postal_code)
Region (region)
Retargeting Conversion Type (retargeting_conversion_type)
SDK Version (sdk_version)
Adrevenue Segment (segment)
State (state)
User Agent (user_agent)
Web Event Type (web_event_type)
WIFI (wifi)
```
Here is the required minimum for report types:
Step 4. Turn On AppsFlyer In Attribution Settings
Go to your app settings in your Appodeal account and choose **Attribution Settings**.
**Primary MMP Account** - your MMP account from where we can get attribution data
**Secondary MMP Account** (optional) - this option is needed if you transfer from one MMP account to
another or if you want to test two different MMPsFor the Primary MMP Account, choose your AppsFlyer account.
You can leave the Secondary MMP Account empty.

For Attribution Platform, choose AppsFlyer, Dev Key can be found in your app settings on the
AppsFlyer side, and App ID is on the top of the page in the browser link when you choose the app in
your AppsFlyer account.
Step 5. Set Up Traffic Sources
In order to see data of your campaign, you need to set up your traffic sources on Adjust side such
as [**Meta**](https://help.adjust.com/en/article/skad-facebook-integration)
or [**Google**](https://help.adjust.com/en/article/skad-google-integration), for example.
You can use [**this preset**](https://app.appodeal.com/analytics/reports?q=(f~(INSTALL*_DATE~(from~*2022-01-19~to~*2022-01-25)ATTRIBUTION*_NETWORK*_HID~!*333266372425416704)g~!ATTRIBUTION*_AD*_SET*_HID~~m~!installs~(id~retention*_rate~d~1)(id~retention*_rate~d~3)avg*_full*_time*_per*_user*_per*_day~avg*_full*_session*_length~(id~cumulative*_ad*_arpu~d~0)(id~cumulative*_ad*_arpu~d~3)~view~table~fv~*)~&trace=aab07f79934bd99d)
to check the statistics of your UA campaign.
Step 6. Send Ad Revenue Data To AppsFlyer (Optional)
If you want to send ad revenue data to AppsFlyer, then you need to complete the following steps:
- Contact us via email [**support@appodeal.com**](support@appodeal.com) or the live chat and we will
enable ad revenue sending;
- Complete the steps from [**this guide**](https://support.appsflyer.com/hc/en-us/articles/360004404977-Appodeal-campaign-configuration)
and make sure to enable "**Get Ad Revenue Data**" in **Configuration** → **Integrated Partners** → **Appodeal** → **Ad Revenue**;
Step 7. Set Up Deep Linking powered by OneLink (optional)
OneLink allows you to create thousands of links easily. You can create links with attribution,
redirection, and deep linking capabilities that convert paid users into app users, regardless of
device, operating system, platform and etc.
Please use [this guide](https://support.appsflyer.com/hc/en-us/articles/115005248543) to set up Deep Linking.
:::tip
If you have any questions while integrating, feel free to contact us via
email at [**support@appodeal.com**](support@appodeal.com) or the live chat.
:::
---------
## Demo Application
You can use our **demo analytics app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Firebase
Firebase SDK (firebase-analytics and firebase-config) is used for analytics and remote config for tests and settings.
------------------------
## Firebase Connection
To connect your Firebase account, follow the steps below.
### Step 1. Import Firebase
Firebase SDK is already included in Appodeal SDK (firebase-analytics and firebase-config). You don't need to install it separately.
Complete all the steps from our [integration guide](../get-started). Make sure to integrate Firebase distributed via Appodeal SDK.
### Step 2. Configure Firebase App
You may follow this [guide](https://firebase.google.com/docs/android/setup) to configure your Firebase app.
1. Add classpath qualifier to buildscript → dependencies into your app level build.gradle file.
```kotlin showLineNumbers
buildscript {
dependencies {
// ... other project dependencies
classpath("com.google.gms:google-services:4.3.10")
}
}
```
2. Connect Google Services plugin to your module
```kotlin showLineNumbers
plugins {
// ... other project dependencies
id("com.google.gms.google-services")
}
```
3. Add your `google-services.json` file into the module (app-level) directory of your app from Firebase console.
### Step 3. Set Up Firebase Remote Config In Attribution Settings (Optional)
If you want to use Firebase Remote Config in your app, you can add your
Firebase parameter keys from Firebase console -> Project name -> Remote Config to Firebase Config Keys in Attribution Settings.
### Step 4. Enable Firebase Tracking In Attribution Settings
To enable sending events to Firebase SDK, you need to enable Firebase
Tracking in Attribution Settings.
------------------------
## Demo Application
You can use our **demo analytics app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Meta
Meta SDK (facebook-core) is used for UA (User Acquisition).
:::note
If you are integrating Meta to see UA metrics in our Dashboard, it will work only in connection with Adjust/AppsFlyer.
To connect them, follow [**this guide**](adjust) for Adjust and [**this guide**](appsflyer) for AppsFlyer.
:::
## Meta connection
To connect Meta, follow the steps below.
### Step 1. Import Meta
Meta SDK is already included in Appodeal SDK (facebook-core). You don't need to install it separately.
### Step 2. Configure Meta App
1. You may follow [**this guide**](https://developers.facebook.com/docs/app-events/getting-started-app-events-android)
to configure you Meta app.
2. Open the /app/res/values/strings.xml file and add the following lines.
Remember to replace [APP_ID] with your actual Facebook app ID:
```jsx title=XML showLineNumbers
[APP_ID]
```
3. Add a `meta-data` elements to the application element, you can get your **Client Token** and
**Facebook App ID** using [**this guide**](https://developers.facebook.com/docs/android/getting-started/#app-id):
```jsx title=XML showLineNumbers
...
...
```
### Step 3. Enable Meta Tracking In Attribution Settings
1. You need to go to your app settings in your Appodeal account and choose Attribution Settings.
2. In Meta Settings enable Meta Tracking.
---
## Demo Application
You can use our **demo analytics app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Using Services in Passive Mode
Appodeal SDK already includes services such as Adjust, AppsFlyer, and Firebase, and we initialize
them automatically with Appodeal initialization.
If you want to be able to initialize services and use their methods yourself, then you need to
follow the steps below.
## Contact Us
In order to use services in passive mode, you need to contact our support via live chat or email [**support@appodeal.com**](support@appodeal.com)
with the following information:
- Links to the apps in the store where you want to initialize Adjust/AppsFlyer/Firebase on your own.
## Integrate Adjust, AppsFlyer and Firebase
Complete all the steps from our [**integration guide**](../get-started)., and make sure to include services in your build.
## Add dependencies below to your app-level build.gradle:
```kotlin showLineNumbers
dependencies {
//Adjust
implementation("com.adjust.sdk:adjust-android:4.33.2")
implementation("com.android.installreferrer:installreferrer:2.2")
// Add the following if you are using the Adjust SDK inside web views on your app
implementation("com.adjust.sdk:adjust-android-webbridge:4.33.2")
//AppsFlyer
implementation("com.appsflyer:af-android-sdk:6.9.4")
implementation("com.appsflyer:adrevenue:6.9.1")
//Firebase
//Java
implementation("com.google.firebase:firebase-analytics:21.2.0")
implementation("com.google.firebase:firebase-config:21.2.0")
//Kotlin
implementation("com.google.firebase:firebase-analytics-ktx:21.2.0")
implementation("com.google.firebase:firebase-config-ktx:21.2.0")
}
```
```groovy showLineNumbers
dependencies {
//Adjust
implementation 'com.adjust.sdk:adjust-android:4.33.2'
implementation 'com.android.installreferrer:installreferrer:2.2'
// Add the following if you are using the Adjust SDK inside web views on your app
implementation 'com.adjust.sdk:adjust-android-webbridge:4.33.2'
//AppsFlyer
implementation 'com.appsflyer:af-android-sdk:6.9.4'
implementation 'com.appsflyer:adrevenue:6.9.1'
//Firebase
//Java
implementation 'com.google.firebase:firebase-analytics:21.2.0'
implementation 'com.google.firebase:firebase-config:21.2.0'
//Kotlin
implementation 'com.google.firebase:firebase-analytics-ktx:21.2.0'
implementation 'com.google.firebase:firebase-config-ktx:21.2.0'
}
```
Complete basic integration steps for [**AppsFlyer**](../services/appsflyer), [**Adjust**](../services/adjust)
and [**Firebase**](../services/firebase),
## Initialize Adjust
After you have contacted our support team and got confirmation to go further, you can initialize
Adjust on your own in the onCreate method and use all its methods according to the official
[**documentation**](https://help.adjust.com/en/article/get-started-android-sdk#integrate-the-sdk).
```kotlin showLineNumbers
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initializeAdjust()
}
// Initialize Adjust
fun initializeAdjust() {
val appToken = "YourAppToken"
val environment = AdjustConfig.ENVIRONMENT_PRODUCTION
val config = AdjustConfig(requireContext(), appToken, environment)
config.setLogLevel(LogLevel.VERBOSE)
Adjust.onCreate(config)
Adjust.onResume()
}
```
```java showLineNumbers
public void onCreate() {
super.onCreate();
initializeAdjust();
}
// Initialize Adjust
public void initializeAdjust () {
String appToken = "YourAppToken";
String environment = AdjustConfig.ENVIRONMENT_PRODUCTION;
AdjustConfig config = new AdjustConfig(this, appToken, environment);
config.setLogLevel(LogLevel.VERBOSE);
Adjust.onCreate(config);
Adjust.onResume();
}
```
When running tests, you should ensure that your environment is set to `AdjustConfig.ENVIRONMENT_SANDBOX`.
Change this to `AdjustConfig.ENVIRONMENT_PRODUCTION` before you submit your application to the Google Play.
## Initialize AppsFlyer
After you have contacted our support team and got confirmation to go further, you can initialize
AppsFlyer on your own in the `onCreate` method and use all its methods according to the official
[**documentation**](https://dev.appsflyer.com/hc/docs/integrate-android-sdk) and AppsFlyer ad revenue
[**guide**](https://dev.appsflyer.com/hc/docs/ad-revenue-1).
```kotlin showLineNumbers
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initializeAppsFlyer()
}
// Initialize AppsFlyer
fun initializeAppsFlyer() {
val appsflyerDevKey = "YOUR_AF_DEV_KEY"
AppsFlyerLib.getInstance().setLogLevel(AFLogger.LogLevel.VERBOSE)
AppsFlyerLib.getInstance().init(appsflyerDevKey,
object : AppsFlyerConversionListener{
override fun onConversionDataSuccess(conversionData: MutableMap?) {
//Wait until conversionData was received before run Appodeal.initialize()
}
override fun onConversionDataFail(reason: String?) {}
override fun onAppOpenAttribution(map: MutableMap?) {}
override fun onAttributionFailure(reason: String?) {}
},
this)
AppsFlyerLib.getInstance().start(this, appsflyerDevKey, object : AppsFlyerRequestListener {
override fun onSuccess() {
Log.d(LOG_TAG, "Launch sent successfully, got 200 response code from server")
}
override fun onError(code: Int, error: String) {
Log.d(LOG_TAG, "Start failed. \nError code: $code \nError description: $error")
}
})
val builder = AppsFlyerAdRevenue.Builder(this)
AppsFlyerAdRevenue.initialize(builder.build())
}
```
```java showLineNumbers
public void onCreate() {
super.onCreate();
initializeAppsFlyer();
}
// Initialize AppsFlyer
public void initializeAppsFlyer() {
String appsflyerDevKey = "YOUR_AF_DEV_KEY";
AppsFlyerLib.getInstance().setLogLevel(AFLogger.LogLevel.VERBOSE);
AppsFlyerLib.getInstance().init("appsflyerDevKey",
new AppsFlyerConversionListener() {
@Override
public void onConversionDataSuccess(Map conversionData) {
// Wait until conversionData was received before run Appodeal.initialize()
}
@Override
public void onConversionDataFail(String reason) {}
@Override
public void onAppOpenAttribution(Map map) {}
@Override
public void onAttributionFailure(String reason) {}
},
this);
AppsFlyerLib.getInstance().start(this, appsflyerDevKey, object : AppsFlyerRequestListener {
@Override public void onSuccess() {
Log.d(LOG_TAG, "Launch sent successfully, got 200 response code from server");
}
@Override public void onError(int code, @NonNull String error) {
Log.d(LOG_TAG, "Start failed. \nError code: " + code + "\nError description: " + error);
}
});
String builder = AppsFlyerAdRevenue.Builder(this);
AppsFlyerAdRevenue.initialize(builder.build());
}
```
## Initialize Firebase
After you have contacted our support team and got confirmation to go further, you don't need to
initialize Firebase, as this is already done on our side.
Firebase analytics will work automatically, and you can use any methods you want, from Firebase
Analytics and Firebase Remote-Config.
If you want to use Firebase Remote-Config in your app, then you need to set it up as shown below and
in the official [**documentation**](https://firebase.google.com/docs/remote-config/get-started?platform=android#kotlin+ktx_1).
```kotlin showLineNumbers
// Set up Firebase Remote-Config
fun setUpRemoteConfig() {
val remoteConfig: FirebaseRemoteConfig = Firebase.remoteConfig
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = 3600 // DEFAULT_MINIMUM_FETCH_INTERVAL_IN_SECONDS
}
remoteConfig.setConfigSettingsAsync(configSettings)
}
```
```java showLineNumbers
// Set up Firebase Remote-Config
public void setUpRemoteConfig() {
FirebaseRemoteConfig mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setMinimumFetchIntervalInSeconds(3600) // DEFAULT_MINIMUM_FETCH_INTERVAL_IN_SECONDS
.build();
mFirebaseRemoteConfig.setConfigSettingsAsync(configSettings);
}
```
## Track In-app Purchases
You can track in-app purchase information and send info to Appodeal servers for analytics.
It allows users to group by the fact of purchasing in-apps.
This will help you adjust the ads for such users or turn them off if needed.
To track in-app purchases, please refer to [**this guide**](../advanced/in-app_purchases)
## Event Tracking
Thanks to in-app events, you can track user activity inside your app.
You can keep track of events such as registration, passing levels, purchases, etc., as in-app events.
The implementation of in-app events is mandatory for all post-install analysis purposes.
You can send events to Adjust, AppsFlyer, and Firebase using the methods from our documentation.
---
## Data Security Android
The table below follows Google Play's official **Data safety** taxonomy and
describes the data types collected by the **Appodeal SDK itself**. Collection
depends on your integration and on end-user permissions — for example, location
is collected only when the app declares and is granted a location permission.
You, the publisher, remain responsible for the final Data safety declaration
for your app.
:::note
This page covers only the Appodeal SDK. Any ad network or service you enable
through Appodeal collects data under its own Data safety declaration — account
for those separately when filling out your Data safety form.
:::
## Data Types Collected by Appodeal SDK
Data type
Data collection
Remarks
Location
Approximate location
Optional
Collected only if your app declares a location permission (ACCESS_COARSE_LOCATION / ACCESS_FINE_LOCATION). To opt out, remove it from your manifest.
Precise location
Optional
Collected only if your app declares a location permission (ACCESS_COARSE_LOCATION / ACCESS_FINE_LOCATION). To opt out, remove it from your manifest.
Personal info
Name
No
Not collected
Email address
No
Not collected
User IDs
Optional
Collected only if the app provides a user ID via Appodeal.setUserId().
Address
No
Not collected
Phone number
No
Not collected
Race and ethnicity
No
Not collected
Political or religious beliefs
No
Not collected
Sexual orientation
No
Not collected
Other info
No
Not collected
Financial info
User payment info
No
Not collected
Purchase history
Optional
Collected only if the application passes purchase data to the Appodeal SDK via Appodeal.trackInAppPurchase(...).
Credit score
No
Not collected
Other financial info
No
Not collected
Health and fitness
Health info
No
Not collected
Fitness info
No
Not collected
Messages
Emails
No
Not collected
SMS or MMS
No
Not collected
Other in-app messages
No
Not collected
Photos and videos
Photos
No
Not collected
Videos
No
Not collected
Audio files
Voice or sound recordings
No
Not collected
Music files
No
Not collected
Other audio files
No
Not collected
Files and docs
Files and docs
No
Not collected
Calendar
Calendar events
No
Not collected
Contacts
Contacts
No
Not collected
App activity
App interactions
Yes
Ad interactions (impressions, clicks) collected for advertising.
In-app search history
No
Not collected
Installed apps
No
Not collected
Other user-generated content
No
Not collected
Other actions
No
Not collected
Web browsing
Web browsing history
No
Not collected
App info and performance
Crash logs
No
Not collected
Diagnostics
Yes
Collected for advertising / analytics.
Other app performance data
Yes
Technical device/performance signals (e.g. device model, memory, storage, user-agent) collected for advertising / analytics.
Device or other IDs
Device or other IDs
Yes
The Appodeal SDK uses the Advertising ID (and similar identifiers) for advertising targeting and ad tracking. Also collected: technical device data (e.g. device model, user-agent), network information (provider, connection type), and other identifiers (IP address, MCC-MNC).
---
## COPPA
For purposes of the [Children's Online Privacy Protection Act (COPPA)](http://business.ftc.gov/privacy-and-security/children%27s-privacy)
there is a setting called childDirectedTreatment. If your app is designed for kids you can disable sending user data to ad networks by calling the method below.
Should be called before the SDK initialization.
```kotlin showLineNumbers
Appodeal.setChildDirectedTreatment(value: Boolean?)
```
```java showLineNumbers
Appodeal.setChildDirectedTreatment(@Nullable Boolean value);
```
:::info
Call `setChildDirectedTreatment` with `true` to indicate that you want your content treated as child-directed for purposes of COPPA.
Call `setChildDirectedTreatment` with `false` to indicate that you don't want your content treated as child-directed for purposes of COPPA.
Call `setChildDirectedTreatment` with `null` to indicate that you want to use the COPPA parameter from your application's settings on the [appodeal.com](http://appodeal.com/).
:::
--------
---
## GDPR and CCPA
:::info
Keep in mind that it’s best to contact qualified legal professionals, if you haven’t done so already, to get more
information and be well-prepared for compliance.
:::
[The General Data Protection Regulation](https://gdpr-info.eu/), better known as GDPR, took effect on May 25, 2018.
It's a set of rules designed to give EU citizens more control over their personal data.
Any *businesses established in the EU or with users based in Europe are required to comply with GDPR or risk facing heavy fines*.
The California Consumer Privacy Act (CCPA) went into effect on January 1, 2020.
**We have put together some guidelines to help publishers understand better the steps they need to take to be GDPR compliant.**
:::info You can learn more about GDPR and CCPA and their differences [here](https://iapp.org/resources/article/ccpa-and-gdpr-comparison-chart/).
:::
--------------
## Step 1. Update Privacy Policy
### Include Additional Information To Your Privacy Policy
Don’t forget to add information about IP address and advertising ID collection, as well as
[the link to Appodeal’s privacy policy](https://www.appodeal.com/privacy-policy)
to your app’s privacy policy on the App Store.
To speed up the process, you could use
[privacy policy generators](https://app-privacy-policy-generator.firebaseapp.com/) -
just insert advertising ID, IP address, and location (if you collect users’ location) in the **Personally Identifiable
Information you collect** field (in line with other information about your app) and
[the link to Appodeal’s privacy policy](https://www.appodeal.com/privacy-policy)
in the **Link to the privacy policy of third party service providers used by the app** field.
### Add A Privacy Policy To Your Mobile App
You must add your explicit privacy policies in two places: on your app’s Store Listing page and within your app.
You can find detailed instructions on adding your privacy policy to your app on legal service websites.
For example, Iubenda, the solution tailored to legal compliance, provides
[a comprehensive guide](https://www.iubenda.com/en/help/401-privacy-policy-for-ios-and-macos-apps)
on including a privacy policy in your app.
Make sure that your privacy policy website has an SSL certificate—this point might seem obvious,
but it’s still essential.
Here are two useful resources that you can utilize while working on your app compliance:
- [Privacy, Security and Deception regulations (by Google Play)](https://play.google.com/intl/en-GB_ALL/about/privacy-security-deception/user-data)
- [Recommendations on Developing a Meaningful Privacy Policy (by Attorney General California Department of Justice)](https://oag.ca.gov/sites/all/files/agweb/pdfs/cybersecurity/making_your_privacy_practices_public.pdf)
:::note
Please note that although we’re always eager to back you up with valuable information, we’re not authorized
to provide any legal advice. It’s important to address your questions to lawyers who specialize in this area.
:::
-------------
## Step 2. Configure Stack Consent Manager with TCF v2 Support
:::info
Since `Appodeal SDK 3.2.1` it is fully compatible with Google UMP and supports IAB TCF v2.
:::
In order for Appodeal and our ad providers to deliver ads that are more relevant to your users, as a mobile app
publisher, you need to collect explicit user consent in the regions covered by GDPR.
To get consent for collecting personal data of your users, we suggest you use a ready-made solution -
Stack Consent Manager based on **Google User Messaging Platform (UMP)**.
:::note Configure Google UMP
Before you start, you need to configure Google UMP. Follow [this instruction](/advanced/google-cmp-and-tcfv2-support) to setup a consent form.
:::
## Step 3. Integrate Stack Consent Manager
Stack Consent Manager comes with a pre-made consent window that you can easily present to your users.
That means you no longer need to create your own consent window.
:::info Starting from Appodeal SDK 3.0, Stack Consent Manager is included by default.
**Consent will be requested automatically on SDK initialization**, and consent form will be shown if it is
necessary without any additional calls.
Please keep in mind that Consent will be shown only in the **EU** region, you can use VPN for testing.
:::
This means that Appodeal SDK integration code remains the same:
```kotlin showLineNumbers
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Appodeal.initialize(activity, appKey, adTypes, object : ApdInitializationCallback {
override fun onInitializationFinished(list: List?) {
//Appodeal initialization finished
}
})
}
```
```java showLineNumbers
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Appodeal.initialize(activity, appKey, adTypes, new ApdInitializationCallback() {
@Override public void onInitializationFinished(List extends ApdInitializationError> list) {
//Appodeal initialization finished
}
});
}
```
## Advanced
Stack Consent Manager is included in Appodeal SDK by default. **Consent will be requested automatically
on SDK initialization** , and consent form will be shown if it is necessary without any additional calls.
You can still use your own Consent Manager or customize ours by following the steps below.
If you wish, you can manage and update consent manually using Stack Consent Manager calls.
### Update Consent Status
To update the consent, call the method:
```kotlin showLineNumbers
override fun onCreate(savedInstanceState: Bundle?) {
ConsentManager.requestConsentInfoUpdate(
parameters = ConsentUpdateRequestParameters(
activity = YourActivity@ this,
key = YOUR_APP_KEY,
tagForUnderAgeOfConsent = false,
sdk = "Appodeal",
sdkVersion = Appodeal.getVersion()
),
callback = object : ConsentInfoUpdateCallback {
override fun onUpdated() {
// User's consent status successfully updated.
}
override fun onFailed(error: ConsentManagerError) {
// Initialize the Appodeal SDK with default params.
}
}
)
}
```
```java showLineNumbers
@Override
protected void onCreate(Bundle savedInstanceState) {
ConsentManager.requestConsentInfoUpdate(
new ConsentUpdateRequestParameters(
YourActivity.this,
YOUR_APP_KEY,
false,
"Appodeal",
Appodeal.getVersion()),
new ConsentInfoUpdateCallback() {
@Override
public void onUpdated() {
// User's consent status successfully updated.
}
@Override
public void onFailed(ConsentManagerError error) {
// Initialize the Appodeal SDK with default params.
}
}
);
}
```
:::tip
`requestConsentInfoUpdate` can be requested at any moment of the application lifecycle.
We recommend call request it at the application launch. Multiple request calls are allowed.
:::
:::note
Required parameters:
`YOUR_APP_KEY` - Appodeal app key, you can get it [in your personal account](https://app.appodeal.com/apps);
`ConsentUpdateRequestParameters` - Data class representing the parameters for a consent update
request in the Appodeal Consent Manager. Use this class to encapsulate the necessary information
for updating consent preferences.
Params:
* `activity` - Activity The activity in which the consent update is requested.
* `key` - The key associated with the user for whom the consent is being updated.
* `tagForUnderAgeOfConsent` - Optional. Indicates whether the user is tagged for under the age of consent. Set to true if the user is under the age of consent, otherwise set to false or null.
* `sdk` - Optional. The identifier for the SDK making the consent update request.
* `sdkVersion` - Optional. The version of the SDK making the consent update request.
`ConsentInfoUpdateCallback` - listener for result request.
:::
### Current consent status
After consent info was updated you may check the current consent status:
```kotlin showLineNumbers
val status: ConsentStatus = ConsentManager.status
```
```java showLineNumbers
ConsentStatus status = ConsentManager.getStatus();
```
:::info
Enum class representing the possible consent statuses in the Appodeal Consent Manager.
* `Unknown` - Represents an unknown consent status;
* `Required` - Represents a required consent status;
* `NotRequired` - Represents a not required consent status;
* `Obtained` - Represents an obtained consent status.
:::
### Can show personalized ads
You may check whether ads can be shown based on the current consent status using:
```kotlin showLineNumbers
ConsentManager.canShowAds()
```
```java showLineNumbers
ConsentManager.canShowAds();
```
:::note
If the user is not within the scope of laws restricting the collection of personal data (`ConsentStatus.NotRequired`)
or the consent form has already been displayed and a response from the user has been
received(`ConsentStatus.Obtained`) then `canShowAds()` returns `true`, indicating, that the
ad can be shown, otherwise it returns `false`.
:::
### Load Consent Form
You may load and receive `ConsentForm` using following code:
```kotlin showLineNumbers
ConsentManager.load(
context = YourActivity@this,
successListener = object : OnConsentFormLoadSuccessListener {
override fun onConsentFormLoadSuccess(consentForm: ConsentForm) {
// Consent form was loaded. Now you can display consent form as dialog
}
},
failureListener = object : OnConsentFormLoadFailureListener {
override fun onConsentFormLoadFailure(error: ConsentManagerError) {
// Consent form loading or showing failed. More info can be found in 'error' object
// Initialize the Appodeal SDK with default params.
}
}
)
```
```java showLineNumbers
ConsentManager.load(
YourActivity.this,
new OnConsentFormLoadSuccessListener() {
@Override
public void onConsentFormLoadSuccess(ConsentForm consentForm) {
// Consent form was loaded. Now you can display consent form as dialog
}
},
new OnConsentFormLoadFailureListener() {
@Override
public void onConsentFormLoadFailure(ConsentManagerError error) {
// Consent form loading or showing failed. More info can be found in 'error' object
// Initialize the Appodeal SDK with default params.
}
}
);
```
### Show consent form
After the consent window Is ready you can show it.
```kotlin showLineNumbers
consentForm.show(
activity = InterstitialActivity@this,
listener = object : OnConsentFormDismissedListener {
override fun onConsentFormDismissed(error: ConsentManagerError?) {
// Consent form loading or showing failed, or it does not required.
// More info can be found in 'error' object
}
}
)
```
```java showLineNumbers
consentForm.show(
InterstitialActivity.this,
new OnConsentFormDismissedListener() {
@Override
public void onConsentFormDismissed(ConsentManagerError error) {
// Consent form loading or showing failed, or it does not required.
// More info can be found in 'error' object
}
}
);
```
### Load and show if required
You may also load the form and immediately show it if required
```kotlin showLineNumbers
ConsentManager.loadAndShowConsentFormIfRequired(
activity = YourActivity@this,
dismissedListener = object : OnConsentFormDismissedListener {
override fun onConsentFormDismissed(error: ConsentManagerError?) {
// Consent form loading or showing failed, or it does not required. More info can be found in 'error' object
}
}
)
```
```java showLineNumbers
ConsentManager.loadAndShowConsentFormIfRequired(
YourActivity.this,
new OnConsentFormDismissedListener() {
@Override
public void onConsentFormDismissed(ConsentManagerError error) {
// Consent form loading or showing failed, or it does not required. More info can be found in 'error' object
}
}
);
```
### Revokes consent
You may reset the consent status to `unknown`, using method:
```kotlin showLineNumbers
ConsentManager.revoke(context = YourActivity@this)
```
```java showLineNumbers
ConsentManager.revoke(YourActivity.this)
```
:::info
Params:
`context` - The context in which consent is revoked.
:::
### US State Regulations Support (Privacy Entry Point)
:::info Available since Appodeal SDK 4.2.0.
:::
US state privacy laws (CCPA, CPA, VCDPA, and others) follow an **opt-out model**: data processing is
allowed by default, but users must be given a permanent way to opt out — typically a
**"Do Not Sell or Share My Personal Information"** button (the *Privacy Entry Point*). In the US zone
the consent form is **not** shown automatically on SDK initialization, because consent is not required
at launch — the opt-out form must be shown on demand, in response to a user tap.
To support this, Stack Consent Manager exposes two methods:
- `ConsentManager.getPrivacyOptionsRequirementStatus()` — tells you whether you must surface a
Privacy Entry Point button in your app UI.
- `ConsentManager.showPrivacyOptionsForm(activity, listener)` — shows the US opt-out form (or the
GDPR re-consent form when called in the EEA).
Both methods become available after `requestConsentInfoUpdate` completes.
#### Check whether a Privacy Entry Point is required
Use `getPrivacyOptionsRequirementStatus()` to decide whether to render the opt-out button. It returns
`PrivacyOptionsRequirementStatus.Required` for users in regulated US states and in the EEA (for GDPR
re-consent), `PrivacyOptionsRequirementStatus.NotRequired` elsewhere, and
`PrivacyOptionsRequirementStatus.Unknown` before `requestConsentInfoUpdate` has completed.
```kotlin showLineNumbers
if (ConsentManager.getPrivacyOptionsRequirementStatus() == PrivacyOptionsRequirementStatus.Required) {
// Show a "Do Not Sell or Share My Personal Information" / Privacy Settings button
}
```
```java showLineNumbers
if (ConsentManager.getPrivacyOptionsRequirementStatus() == PrivacyOptionsRequirementStatus.Required) {
// Show a "Do Not Sell or Share My Personal Information" / Privacy Settings button
}
```
#### Show the Privacy Options form
Call `showPrivacyOptionsForm` from the click handler of your Privacy Entry Point button. This is the
**only** way to display the US opt-out form, it must be triggered by an explicit user interaction — not
on SDK initialization — and it must be called on the main thread.
```kotlin showLineNumbers
privacyOptionsButton.setOnClickListener {
ConsentManager.showPrivacyOptionsForm(
activity = YourActivity@this,
listener = object : OnConsentFormDismissedListener {
override fun onConsentFormDismissed(error: ConsentManagerError?) {
// A non-null 'error' means the form could not be presented.
// Otherwise the user finished interacting with the form.
}
}
)
}
```
```java showLineNumbers
privacyOptionsButton.setOnClickListener(v ->
ConsentManager.showPrivacyOptionsForm(
YourActivity.this,
new OnConsentFormDismissedListener() {
@Override
public void onConsentFormDismissed(ConsentManagerError error) {
// A non-null 'error' means the form could not be presented.
// Otherwise the user finished interacting with the form.
}
}
)
);
```
:::note
Once the user interacts with the US opt-out form, Stack Consent Manager writes the corresponding
`IABGPP_*` keys to the default `SharedPreferences`, where ad networks read them. Before the form has
been shown at least once, these keys remain empty and ad networks may treat the user as
"no consent collected".
:::
### Non-Personalized Advertising
:::info Available since Appodeal SDK 4.3.0.
:::
Consent is enforced automatically — no extra integration is required.
If you want to request non-personalized advertising regardless of the resolved consent, call
`Appodeal.setNonPersonalized(true)`. This disables the collection of data used for ad personalization,
and a publisher-set value takes precedence over the consent resolved from the CMP.
Call it before `Appodeal.initialize(...)`.
This is relevant in several scenarios:
- **Age-restricted users (US).** US state laws (CCPA/CPRA in California, and similar laws in Virginia,
Colorado, Connecticut, and others) restrict selling or sharing the personal data of minors, and COPPA
adds stricter rules for children under 13. Use this flag alongside
[`setChildDirectedTreatment`](coppa) when you cannot determine the exact age but targeting must be
limited.
- **Users who declined personalized advertising** through your own consent flow, when they are not
subject to a specific regulation covered by the other APIs.
- **General opt-out** — a catch-all to suppress targeting signals when none of the more specific privacy
flags apply.
```kotlin showLineNumbers
Appodeal.setNonPersonalized(true)
```
```java showLineNumbers
Appodeal.setNonPersonalized(true);
```
---
## Ad Revenue Callbacks
Appodeal SDK allows you to get impression-level revenue data with Ad Revenue Callbacks.
This data includes information about network name, revenue, ad type, etc.
The impression-level ad revenue data can be used then to share with your
mobile measurement partner of choice, such as [Firebase](../services/firebase), for all
supported networks.
If you have integrated Firebase, which is included in Appodeal SDK,
using this [guide](../services/firebase), then ad revenue data will be sent automatically, you
can read more about it [here](launching-troas#step-2-set-up-your-firebase-account) in Step 2.
:::info Minimum Requirements:
Appodeal SDK 3.0.1+
:::
## Callback Implementation
```kotlin showLineNumbers
Appodeal.setAdRevenueCallbacks(object : AdRevenueCallbacks {
override fun onAdRevenueReceive(revenueInfo: RevenueInfo) {
// Called whenever SDK receives revenue information for an ad
}
}
```
```java showLineNumbers
Appodeal.setAdRevenueCallbacks(new AdRevenueCallbacks() {
@Override
public void onAdRevenueReceive(@NotNull RevenueInfo revenueInfo) {
// Called whenever SDK receives revenue information for an ad
}
});
```
:::note
All callbacks are called on the main thread.
:::
:::note Admob Notice
To get impression-level ad revenue from Admob you also need to turn on the setting in your [AdMob account](https://apps.admob.com/v2/settings/account-info).
Go to your **Admob Account Settings** → **Account** → turn on **Impression-level ad revenue toggle**.
:::
## Appodeal Ad Revenue Description
`RevenueInfo` - represents revenue information from the ad network.
| Parameter | Type | Description |
|------------------|-----------------|-----------------------------------------------------------------------------------------------------------------|
| networkName | String | The name of the ad network is guaranteed not to be null. |
| demandSource | String | The demand source name and bidder name in case of impression from real-time bidding, guaranteed not to be null. |
| adUnitName | String | Unique ad unit name guaranteed not to be null. |
| placement | String | Appodeal's placement name is guaranteed not to be null. |
| revenue | Double | The ad's revenue amount or 0 if it doesn't exist. |
| adType | Int | Appodeal's ad type. |
| adTypeString | String | Appodeal's ad type as string presentation. |
| platform | String | Appodeal's platform name. |
| revenueCurrency | RevenueCurrency | Current currency supported by Appodeal (USD). |
| currency | String | Current currency supported by Appodeal (USD) as string presentation. |
| revenuePrecision | String | The revenue precision. |
:::info
The revenue precision can be:
1. **exact** - programmatic revenue is the resulting price of the auction;
2. **publisher_defined** - revenue from crosspromo campaigns;
3. **estimated** - revenue based on ad network pricefloors or historical eCPM;
4. **undefined** - revenue amount is not defined.
:::
## Use Case
:::info Please remember
If you have integrated analytics for example Firebase using this
[guide](../services/firebase) with Appodeal, then no additional steps are
required.
:::
In case you are using your own analytics in the project, please find the example below:
```kotlin showLineNumbers
override fun onAdRevenueReceive(revenueInfo: RevenueInfo) {
// AppsFlyer
val customParams = mapOf(
Scheme.AD_UNIT to revenueInfo.adUnitName,
Scheme.AD_TYPE to revenueInfo.adTypeString,
)
AppsFlyerAdRevenue.logAdRevenue(
revenueInfo.networkName,
MediationNetwork.appodeal,
Currency.getInstance(Locale.US),
revenueInfo.revenue,
customParams
)
// Adjust
val adRevenue = AdjustAdRevenue(AdjustConfig.AD_REVENUE_SOURCE_PUBLISHER).apply {
setRevenue(revenueInfo.revenue, revenueInfo.currency)
setAdRevenueNetwork(revenueInfo.networkName)
setAdRevenueUnit(revenueInfo.adUnitName)
}
Adjust.trackAdRevenue(adRevenue)
// Firebase
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.AD_IMPRESSION) {
param(FirebaseAnalytics.Param.AD_PLATFORM, revenueInfo.platform)
param(FirebaseAnalytics.Param.SOURCE, revenueInfo.networkName)
param(FirebaseAnalytics.Param.AD_FORMAT, revenueInfo.adTypeString)
param(FirebaseAnalytics.Param.AD_UNIT_NAME, revenueInfo.adUnitName)
param(FirebaseAnalytics.Param.CURRENCY, revenueInfo.currency)
param(FirebaseAnalytics.Param.VALUE, revenueInfo.revenue)
}
}
```
```java showLineNumbers
@Override
public void onAdRevenueReceive(@NotNull RevenueInfo revenueInfo) {
// AppsFlyer
Map customParams = new HashMap<>();
customParams.put(Scheme.AD_UNIT, revenueInfo.getAdUnitName());
customParams.put(Scheme.AD_TYPE, revenueInfo.getAdTypeString());
AppsFlyerAdRevenue.logAdRevenue(
revenueInfo.getNetworkName(),
MediationNetwork.appodeal,
Currency.getInstance(Locale.US),
revenueInfo.getRevenue(),
customParams
);
// Adjust
AdjustAdRevenue adRevenue = new AdjustAdRevenue(AdjustConfig.AD_REVENUE_SOURCE_PUBLISHER)
adRevenue.setRevenue(revenueInfo.getRevenue(), revenueInfo.getCurrency());
adRevenue.setAdRevenueNetwork(revenueInfo.getNetworkName());
adRevenue.setAdRevenueUnit(revenueInfo.getAdUnitName());
Adjust.trackAdRevenue(adRevenue);
// Firebase
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.AD_PLATFORM, revenueInfo.getPlatform());
bundle.putString(FirebaseAnalytics.Param.SOURCE, revenueInfo.getNetworkName());
bundle.putString(FirebaseAnalytics.Param.AD_FORMAT, revenueInfo.getAdTypeString());
bundle.putString(FirebaseAnalytics.Param.AD_UNIT_NAME, revenueInfo.getAdUnitName());
bundle.putString(FirebaseAnalytics.Param.CURRENCY, RevenueInfo.getCurrency());
bundle.putString(FirebaseAnalytics.Param.VALUE, RevenueInfo.getRevenue());
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.AD_IMPRESSION, bundle);
}
```
---
## Ad Revenue Forwarding to MMP/BI
Appodeal SDK allows you to get ad revenue data using
[Ad Revenue Attribution API](../../../advanced/ad-revenue-attribution) and [Ad Revenue Callbacks](ad-revenue-callback).
This data includes information about network name, revenue, ad type, etc.
It is possible to send ad revenue data to Adjust, AppsFlyer, and also to your own MMP/BI.
To send ad revenue data to MMP/BI, please follow the steps below:
## Adjust
No additional steps are required if you have integrated Adjust using this [guide](../services/adjust).
Ad revenue data will be sent automatically after the ad impression.
If you want to send ad revenue data to Adjust you need to use the code below :
```
val adRevenue = AdjustAdRevenue(AdjustConfig.AD_REVENUE_SOURCE_PUBLISHER).apply {
setRevenue(revenueInfo.revenue, revenueInfo.currency)
setAdRevenueNetwork(revenueInfo.networkName)
setAdRevenueUnit(revenueInfo.adUnitName)
}
Adjust.trackAdRevenue(adRevenue)
```
## AppsFlyer
**Ad Revenue Attribution API**:
- If you want to send ad revenue to AppsFlyer, please follow step 6 from the [AppsFlyer](../services/appsflyer) guide.
**Ad Revenue Callbacks**:
- Please refer to the [guide](ad-revenue-callback) in this case.
## Own MMP/BI
**Ad Revenue Attribution API**:
- Contact us via email at [support@appodeal.com](mailto:support@appodeal.com) or the live chat, and we will enable ad
revenue sending;
- Send your attribution ID to Appodeal using `Appodeal.setExtraData` method from this [guide](user-data);
- To get ad revenue data, you need to follow this [guide](../../../advanced/ad-revenue-attribution);
- Then you can send received ad revenue data to your MMP/BI.
**Ad Revenue Callbacks**:
- Please refer to the [guide](ad-revenue-callback) in this case.
---
## Configure Mediated Networks
Select the ad types that are used in your application and the ad networks
that you want to include, and select all services if you use **Appodeal SDK Full Package**.
You can add or remove any ad network or service adapter depending on your requirements.
If you want to use **Appodeal as a mediation only**, you may to **exclude** services from the build.
---
## Debug test mode
**Starting from SDK 2.8.0 we've added new logic to enable test mode via ADB commands, which can be
used with Debug and Release builds of apps.**
| Command | Values | Description |
|-----------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
| `adb shell setprop debug.appodeal.sdk.testmode` | `true / false / .none.` | Command for set `Test Mode` |
| `adb shell setprop debug.appodeal.sdk.log` | `true / false / .none.` | Command for enabling/disabling logs and internal logs by `D/Appodeal` and `D/InternalLogs` tags |
| `adb shell setprop debug.appodeal.sdk.loglevel` | `none / debug / verbose / .none.` | Command for set logging level |
| `adb shell setprop debug.appodeal.sdk.url` | String in a proper format (e.g -https://staging2.appodeal.com:443) | Command for set Appodeal SDK request url |
| `adb shell setprop debug.appodeal.sdk.testactivity` | `true / false / .none.` | Command for enabling `Test Activity` starting on SDK initialization |
| `adb shell setprop debug.appodeal.sdk.networks` | An array of strings, with comma divider (e.g - admob,unity_ads,...) | Command for set enabled ad networks |
`.none.` - is the default state for all commands and related parameters
:::tip
Example:
**adb shell setprop debug.appodeal.sdk.log true**
**adb shell setprop debug.appodeal.sdk.loglevel verbose**
:::
---
## Event Tracking
## Introduction
Thanks to in-app events, you can track user activity inside your app.
You can keep track of events such as registration, passing levels,
purchases, etc., as in-app events. The implementation of in-app events
is mandatory for all post-install analysis purposes.
## Types Of Events
In-app events can be divided into two categories:
- **Basic in-app events** are standard in-app events that help you understand user activity inside your app.
**Examples:**
```text
level1_finished
level2_start
app_login
```
- **Rich in-app events** are the same as basic in-app events but let you
get more detailed information about the event through a number of
parameters. You will learn more about them in step 1. Through
parameters, you can send additional information about the event. For
example, you can not only learn that app was opened but also the
exact date and time.
**Examples:**
```text
level1_finished (result)
level2_start (time)
app_login (date)
```
## Recommended Events
You need to select the events that best suit your application.
:::info Recommendations
- For better navigation through reports, we recommend using the same event names in your app across all platforms.
- Create all kinds of events with a maximum number of details that describe user actions in your application.
- We recommend using only lower-case alpha-numeric characters (a-z and 0-9) for your in-app event names.
:::
**Examples (for other apps):**
```text
appodeal_initialized
complete_registration
user_login
tutorial_completion
on_search
content_view
in_app_purchase
```
**Examples (for games):**
```text
game_start
game_win
game_end
main_menu_open
game_lose
round_start
round_end
pause_menu_open
design_dialog_open
settings_dialog_open
design_application_changed
level1_complete
appodeal_consent_dialog_open
appodeal_consent_dialog_result
```
# Step 1. How To Track In-app Events
Appodeal SDK allows you to send events to the following analytic services using a single method:
- [Firebase](../services/firebase);
- [AppsFlyer](../services/appsflyer);
- [Adjust](../services/adjust);
- [Meta](../services/meta).
Use this method for send event for all connected services:
```kotlin showLineNumbers
// Create map of event parameters if required
val params = mapOf(
"example_param_1" to "value",
"example_param_2" to 123
)
Appodeal.logEvent(eventName = "appodeal_sdk_test_event", params = params)
```
```java showLineNumbers
// Create map of event parameters if required
Map params = new HashMap<>();
params.put("example_param_1", "value");
params.put("example_param_2", 123);
Appodeal.logEvent("appodeal_sdk_test_event", params);
```
Use this method for send event for a specific service:
```java showLineNumbers
// Create map of event parameters if required
val params = mapOf(
"example_param_1" to "value",
"example_param_2" to 123
)
Appodeal.logEvent(
eventName = "appodeal_sdk_test_event",
params = params,
service = AppodealServices.APPSFLYER or AppodealServices.FIREBASE
)
```
```java showLineNumbers
// Create map of event parameters if required
Map params = new HashMap<>();
params.put("example_param_1", "value");
params.put("example_param_2", 123);
Appodeal.logEvent("appodeal_sdk_test_event", params, AppodealServices.APPSFLYER | AppodealServices.FIREBASE);
```
------------------
:::info Please note
You can use one or more of these values to select the service to send the event to:
- `AppodealServices.ADJUST` - for Adjust service;
- `AppodealServices.APPSFLYER` - for Appsflyer service;
- `AppodealServices.FACEBOOK` - for Facebook service;
- `AppodealServices.FIREBASE` - for Firebase service;
- `AppodealServices.ALL` - for ALl services;
or you may combine these values, for example, to sent event to Appsflyer and Firebase services:
- `AppodealServices.APPSFLYER | AppodealServices.FIREBASE` - for kotlin
- `AppodealServices.APPSFLYER or AppodealServices.FIREBASE` - for java
:::
:::info Please note
Event parameters can only be strings and numbers, they allow you to send
additional information about the event in your app.
:::
# Step 2. Configure In-app Events
Some additional steps may be needed on the MMP side to complete events setup.
## Appodeal Free Adjust Account
- If you want to send events to Adjust, contact our support team via
email [support@appodeal.com](mailto:support@appodeal.com) or in the live chat and send us the list
with event names.
By default, Appodeal SDK sends s2s events to Adjust.
The list of s2s events :
- `dc_cpa_event_d0` - this event includes the ARPU of Day 0 after the app install;
- `dc_cpa_event_d2` - this event includes the ARPU of Day 2 after the app install;
- `dc_cpa_event_d7` - this event includes the ARPU of Day 7 after the app install;
- `dc_cpa_event_d30` - this event includes the ARPU of Day 30 after the app install.
:::info
If you want to target those s2s events in your UA campaigns, please
contact our support team via email [support@appodeal.com](mailto:support@appodeal.com) or in the live
chat so we can connect those events with your Traffic Source.
:::
## Own Adjust Account
If you want to send events to Adjust you need to create your events on
Adjust side according to this [guide](https://help.adjust.com/en/article/basic-event-setup)
and **send their tokens** to us via email
[support@appodeal.com](mailto:support@appodeal.com) or in the live chat:
- Find your app in the dashboard and select your app options caret (^);
- Select **All Settings > Events**;
- Find the **Create New Event** label at the bottom of the module and enter your event name;
- Select **Create**;
- **Send us the token** of each event specifying the event name(you can find the token right next to the event in **All
Settings > Events**);
You also need to create some required SDK events presented below:
**Required SDK events:**
- `hs_sdk_purchase` - in-app purchase was validated successfully;
- `hs_sdk_unknown` - unknown event;
- `hs_sdk_purchase_error` - in-app purchase wasn't validated, error occurred.
## Own AppsFlyer Account
- No additional steps are required.
---
## In-App purchases
## Automatic verification and sending of purchase information
:::info
Starting Appodeal SDK 3.7.0+, it is possible to automatically verify and submit purchases/subscription to
Appodeal, as well as receive purchase information from the Appodeal SDK using [Appsflyer](../services/appsflyer).
:::
To activate this feature contact us via email at [support@appodeal.com](mailto:support@appodeal.com)
or the live chat, and ask to enable ad **roi360** feature
:::note
Automatic verification of purchases works with versions of
[Google Play Billing Library 5-7](https://developer.android.com/google/play/billing/integrate).
:::
Add the following dependency into your module-level **build.gradle:**
```kotlin title=build.gradle showLineNumbers
buildscript {
dependencies {
// ... other project dependencies
implementation("com.android.billingclient:billing:5.0.0")
// or
implementation("com.android.billingclient:billing-ktx:5.0.0")
}
}
```
```groovy title=build.gradle showLineNumbers
buildscript {
dependencies {
// ... other project dependencies
implementation 'com.android.billingclient:billing:5.0.0'
// or
implementation 'com.android.billingclient:billing-ktx:5.0.0'
}
}
```
For automatic verification of purchases you need to generate and provide our team with **JSON key**
from **Google Cloud Platform**, as well as set up the ability to receive information about purchases
from **Google Play Developer Console**.
To do this, you need to perform the following steps:
### Step 1. link your Google Play Developer Account to Google Cloud Project
* In the Google Play Console, go to your Google Play Developer Account.
* Link the account to your Google Cloud project. For instructions, [see this Google help topic](https://developers.google.com/android-publisher/getting_started#linking).
* Enable the Google Play Developer API. For instructions, see [this Google help topic](https://developers.google.com/android-publisher/getting_started#enable).
### Step 2. Configure your Service Account in the Google Cloud Platform
Prerequisites: **Access to Google Cloud Platform**.
To configure your service account:
#### 2.1 Create or locate the Service Account:
1. In the Google Cloud Platform, go to the **Service accounts** section and click **Create Service Account**.
2. Complete the service account details.
3. Copy the email address and Click **Create and continue**.
:::note
This email you should invite to Google Play Console(see below)
:::
4. In the **Grant this service account access to the project** step, select the **Pub/sub** subscriber role, after that click **Done**
#### 2.2 Download the Service Account Private Key:
In the Google Cloud Platform, go to the **Service accounts** section, find the account you want
(the one you just created) from the list, and click the **More actions** icon.
1. Click **Manage keys**.
2. Click **Add key** > **Create new key**.
3. In the **Create private key** popup, under **Key type**, select **JSON**, and click **Create**.
4. Click **Create**. The private key JSON file is downloaded.
Save the JSON key file, which will be uploaded to AppsFlyer later.
:::note
You must save the key; it can't be retrieved later. If you don't save it, you will have to create an entirely new key.
**Please share this JSON file with Appodeal team. We will need it to connect Google Play Console with our Appsflyer account.**
:::
### Step 3. Set API access permissions in Google Play Console
Prerequisites: Access to Google Play Console.
:::note
It can take time (sometimes even 24 hours) after setting service account credentials and permissions
to be able to use them. This may cause you to receive errors in later steps.
:::
**To set API access permissions in Google Play Console:**
1. In the Google Play Console, go to **Users and permissions**, find the service account you created, and click **Invite new users**.
2. Enter the **Email address** that you copied when you configured your service account in step 2.1.3
3. In the **Permissions** section, go to the **Account permissions** tab, and select following:
* View app information and download bulk reports(read-only)
* View financial data, orders, and cancellation survey responses.
* Manage orders and subscriptions.
4. Click **Invite user**.
5. In the confirmation popup, click **Send invite**.
### Step 4. Send Google Play notifications directly to AppsFlyer
Prerequisites: Access to Google Play Console and AppsFlyer UI.
1. In **Google Play Console** > **Home**, select to view your app. The dashboard opens.
2. Go to **Monetize with Play** > **Monetization setup**, and in the **Google Play Billing section**, make sure
**Enable real-time notification** is checked.
3. In the **Topic name** field, paste the AppsFlyer topic address:
**projects/appsflyer-ars/topics/subscription-events**
4. For **Notification content**, select **Subscriptions, voided purchases, and all one-time products**.
5. Click **Save Changes**
:::note
It can take time (sometimes even 24 hours) for this to take effect. Therefore, wait before testing.
:::
### Step 5. Contact Us
After all completed steps contact our support team via email [support@appodeal.com](mailto:support@appodeal.com)
or a live chat with the following information :
1. App for which you want to enable ad **roi360** feature
2. Service account JSON file from Step 2.2;
### Optional. Step 6 Setting up AppodealPurchaseCallback
Once the functionality is enabled and configured, when a purchase is made in your app via Google Billing Library,
Appodeal SDK will automatically detect, verify and send the inApps/subscription details to Appodeal Dashboard.
If you want to receive purchase information in your app, you just need to call the method:
```kotlin showLineNumbers
Appodeal.setPurchaseListener(object: AppodealPurchaseListener{
override fun onPurchaseReceived(successPurchases: List
``` java showLineNumbers
Appodeal.setPurchaseListener(new AppodealPurchaseListener() {
@Override
public void onPurchaseReceived(@NonNull List> successPurchases) {
Log.d("Appodeal App", "onPurchaseReceived: " + successPurchases);
}
@Override
public void onPurchaseFailed(@NonNull Throwable reason,
@Nullable List> failedPurchases) {
Log.d("Appodeal App", "Message: " + reason.getMessage() + " failedPurchases: " + failedPurchases);
}
});
}
```
The purchase object you get in onPurchaseReceived/onPurchaseFailed is a list of objects
* for **InApps** purchase:
| Parameter | Description
|------------------------------------|----------------------------------------------------------------------------------------------------------|
| **purchase_token** | Token of received purchase |
| **product_ids** | List of purchase ids, |
| **product_type** | Type of purchase inapp/subs |
| **purchase_time** | Timestamp of purchase |
| **package_name** | App package, where purchase was done |
| **one_time_purchase_offer_details**| Map details of purchase, contains "formatted_price", long "price_amount_micros", "price_currency_code" |
| **quantity** | Count of purchase items |
| **order_id** | Order id of received purchase |
| **region_code** | Code of country of received purchase |
* for **Subscription** purchase:
| Parameter | Description
|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
| **purchase_token** | Token of received subscription |
| **product_ids** | List of subscription ids, |
| **product_type** | Type of purchase inapp/subs |
| **purchase_time** | Timestamp of purchase |
| **subscription_offer_details** | List of maps which contains details of subscription offers, like "base_plan_id", "offer_tags", "offer_token", "pricing_phases" | |
| **package_name** | app package, where purchase was done |
| **one_time_purchase_offer_details**| Map details of purchase, contains "formatted_price", long "price_amount_micros", "price_currency_code" |
| **latest_order_id** | Order id of received subscription |
| **subscription_state** | Current state of description, example: active |
| **start_time** | Start date of subscription |
| **expiry_time** | Expiration date of subscription |
At this point, the connection of automatic purchases is fully completed.
:::note
Purchase reports will be automatically uploaded to the Dashboard of your personal cabinet 2 times a day
:::
## Manual verification and sending of purchase information
:::info
In-App purchase tracking will work only in connection with
Adjust/AppsFlyer. To connect them, follow [this guide](../services/adjust)
for Adjust and [this guide](../services/appsflyer) for
AppsFlyer.
:::
It's possible to track in-app purchase information and send info to
Appodeal servers for analytics. It allows to group users by the fact of
purchasing in-apps. This will help you to adjust the ads for such users
or simply turn it off, if needed. To make this setting work correctly,
please submit the purchase info via the Appodeal SDK.
### Step 1. Validate In-app Purchases
To track in-app purchase, Appodeal SDK will need info about purchases provided by
the [Google Play Billing Library](https://developer.android.com/google/play/billing/integrate)
Add the following dependency into your module-level **build.gradle:**
```kotlin title=build.gradle showLineNumbers
buildscript {
dependencies {
// ... other project dependencies
implementation("com.android.billingclient:billing:5.0.0")
// or
implementation("com.android.billingclient:billing-ktx:5.0.0")
}
}
```
```groovy title=build.gradle showLineNumbers
buildscript {
dependencies {
// ... other project dependencies
implementation 'com.android.billingclient:billing:5.0.0'
// or
implementation 'com.android.billingclient:billing-ktx:5.0.0'
}
}
```
Get your Purchase and SkuDetails objects from the Google Play Billing Library
using [the guide](https://developer.android.com/google/play/billing/integrate).
Get a price and currency from SkuDetails.
Create and validate in-app purchase using the method below:
```kotlin showLineNumbers
// Purchase object is returned by Google API in onPurchasesUpdated() callback
fun validatePurchase(purchase: Purchase) {
// Create new InAppPurchase with type
val inAppPurchase: InAppPurchase = InAppPurchase.newBuilder(type = InAppPurchase.Type.InApp)
.withPublicKey("YOUR_PUBLIC_KEY")
.withSignature(purchase.signature)
.withPurchaseData(purchase.originalJson)
.withPurchaseToken(purchase.purchaseToken)
.withPurchaseTimestamp(purchase.purchaseTime)
.withDeveloperPayload(purchase.developerPayload)
.withOrderId(purchase.orderId)
.withSku(...) // Stock keeping unit id from Google API
.withPrice(...) // Price from Stock keeping unit
.withCurrency(...) // Currency from Stock keeping unit
.withAdditionalParams(...) // Appodeal In-app event if needed
.build()
// Validate Purchase
Appodeal.validateInAppPurchase(context = context, purchase = inAppPurchase, callback = object : InAppPurchaseValidateCallback {
override fun onInAppPurchaseValidateSuccess(purchase: InAppPurchase, errors: List?) {
// In-App purchase validation was validated successfully by at least one connected service
}
override fun onInAppPurchaseValidateFail(purchase: InAppPurchase, errors: List) {
// In-App purchase validation was failed by all connected service
}
})
}
```
``` java showLineNumbers
// Purchase object is returned by Google API in onPurchasesUpdated() callback
public void validatePurchase(Purchase purchase) {
// Create new InAppPurchase
InAppPurchase inAppPurchase = InAppPurchase.newBuilder("PURCHASE_TYPE")
.withPublicKey("YOUR_PUBLIC_KEY")
.withSignature(purchase.getSignature())
.withPurchaseData(purchase.getOriginalJson())
.withPurchaseToken(purchase.getPurchaseToken())
.withPurchaseTimestamp(purchase.purchaseTime)
.withDeveloperPayload(purchase.getDeveloperPayload())
.withOrderId(purchase.getOrderId())
.withSku(...) // Stock keeping unit id from Google API
.withPrice(...) // Price from Stock keeping unit
.withCurrency(...) // Currency from Stock keeping unit
.withAdditionalParams(...) // Appodeal In-app event if needed
.build()
// Validate InApp purchase
Appodeal.validateInAppPurchase(this, inAppPurchase, new InAppPurchaseValidateCallback() {
@Override
public void onInAppPurchaseValidateSuccess(@NonNull InAppPurchase purchase, @Nullable List errors) {
// In-App purchase validation was validated successfully by at least one connected service
}
@Override
public void onInAppPurchaseValidateFail(@NonNull InAppPurchase purchase, @NonNull List errors) {
// In-App purchase validation was failed by all connected service
}
});
}
```
:::info
Please make sure if you have created in-app product in Google Play Console → Monetize section to use:
- **InAppPurchase.Type.InApp** for purchase type
- **InAppPurchase.Type.Subs for** subscription
:::
| Parameter | Description | Usage |
|--------------------------|-----------------------------------------------------------------------------------------------------------------------|---------------------------|
| **purchaseType** | Purchase type. Must be *InAppPurchase.Type.InApp* or *InAppPurchase.Type.Subs* | Adjust/AppsFlyer |
| **publicKey** | [Public key from **Google Developer Console**](https://support.google.com/googleplay/android-developer/answer/186113) | AppsFlyer |
| **signature** | Transaction signature (returned from Google API when the purchase is completed) | Adjust/AppsFlyer |
| **purchaseData** | Product purchased in JSON format (returned from Google API when the purchase is completed) | AppsFlyer |
| **purchaseToken** | Product purchased token (returned from Google API when the purchase is completed) | Adjust |
| **purchaseTimestamp** | Product purchased timestamp (returned from Google API when the purchase is completed) | Adjust |
| **developerPayload** | Product purchased developer payload (returned from Google API when the purchase is completed) | Adjust |
| **orderId** | Product purchased unique order id for the transaction (returned from Google API when the purchase is completed) | Adjust |
| **sku** | Stock keeping unit id. | Adjust |
| **price** | In-app event revenue. | Adjust/AppsFlyer/Appodeal |
| **currency** | In-app event currency. | Adjust/AppsFlyer/Appodeal |
| **additionalParameters** | Additional parameters of the in-app event.
:::info In-App purchase validation runs by FIFO queue in a single thread.
:::
:::note
If you are using **your own Adjust** account you need to complete Step 2
from our Event Tracking [guide](event-tracking) and create some required events on Adjust side.
:::
### Step 2. Generate Json File In Google Cloud
1. Login to Google Cloud with your credentials;
2. Select **Google Play Console Developer project** on the top left corner as shown below;
:::note
Please make sure to select **Google Play Console Developer** project at this step instead of your
exact app project. Google Play Console only allows to link **Google Play Console Developer**
cloud projects (later in step 3).
:::
3. Select Credentials → Create Credentials → select Service Account;
4. Select Viewer as a role for Service Account and press Done;
5. Go to your service account and press keys → Add key → choose JSON → and send us the JSON file
via email [support@appodeal.com](support@appodeal.com) or a live chat.
### Step 3. Add Required Permissions In Google Play Console
1. Go to the [** Google Play Console**](https://play.google.com/apps/publish/) and log in;
2. Go to Google Play Console → Manage developer accounts → Choose developer account → Setup → API
Access and choose your **Google Play Console Developer** project from step 2 where you have created
your Service Account;
If you are not able to see **Google Play Console Developer** project in the list then please update
the webpage.
:::tip
If the issue persists, make sure that your Google Play developer account(email) is the owner of the
Google Cloud project. You can read more [**here**](https://developers.google.com/android-publisher/getting_started).
:::
3. At the bottom there will be a list of service accounts that are available in this Google Cloud
project. Select the one from which the JSON was sent;
Press **Refresh** if you are not able to see your Service Account.
4. Press **View Play Console** permissions. In the **App Permissions** section select the necessary
applications where in-app events will be used;
5. Go to **Account Permissions** section and select all **Financial Data** permissions:
- **View financial data**
- **Manage Orders, subscriptions**
### Step 4. Contact Us
After all completed steps contact our support team via email [support@appodeal.com](mailto:support@appodeal.com)
or a live chat with the following information :
1. Service account JSON file;
2. Purchases implementation logic in your app (when and where you call validate method and validate
purchases);
3. Send us the purchase testing access through the Google Developer console to email
[support@appodeal.com](support@appodeal.com);
4. Your apk in a zip for testing.
### Step 5. Testing
After you have contacted our Support Team and provided all the required information you can test your
app to make sure purchases are validated.
1. Please go to your App Settings → Attribution Settings → and change Adjust Environment from
Production to **Sandbox** to be able to test validation and don't forget to press **Save** at the
end of the page;
2. Connect your device to your computer with the opened console (Android Studio logcat) and tag
logs by *purchase*;
3. Now you can open your App and make a test purchase, if you can see **Valid purchase** in the
console, then validation went successfully;
4. If validation has failed, then please recheck all the steps above;
5. After testing, change your Adjust Environment to **Production** in App Settings → Attribution Settings.
---
## Launching a tROAS campaign in Google Ads
**tROAS (target Return On Ad Spend )** is Google's smart bidding strategy that uses auction-time bidding to reach your
specified value.
Target ROAS regulates bids to maximize the value of your conversions.
By simply integrating Appodeal SDK in your app, you will be able to send ad revenue data to Firebase and launch a tROAS
campaign in Google Ads.
## Step 1. Integrate Firebase
Complete all the steps from our [Firebase integration guide](../services/firebase).
## Step 2. Set Up Your Firebase Account
:::info Make sure you have admin access to your Firebase and Google Ads accounts.
:::
1. You need to link your Firebase project with your Google Adwords account. In order to do so, please go to your *
*Firebase project → Project settings → Integrations → Google Ads → Link→ Choose your Google Ads account**
2. You can use Google Analytics to measure ad revenue generated from displaying ads.
To measure ad revenue, we are logging the **custom_ad_impression** event whenever your user sees an advertisement in
your app.
In Analytics, your most important events are called conversions, and in order to be able to import them
In Analytics, your most important events are called conversions, and in order to be able to import
them to your Google Adwords in the next step, account you need to mark the **custom_ad_impression** event
as a conversion by going to your Firebase project → Analytics → Events.
:::info Event reports are available within 24 hours on the Firebase side
:::
## Step 3. Set Up Your Google Adwords Account
Go to your Google Adwords account → Tools and Settings → Conversions → New Conversions → Import → Google Analytics 4
properties → App and import **first_open** and **custom_ad_impression** conversions.
Now you have set up everything and it is time to create a campaign on Google Ads side.
Check [this guide](https://support.google.com/google-ads/answer/6268637?hl=en) to learn more about tROAS bidding.
---
## Segments and Placements
## Segments
Segments are used to track statistics for various user categories and manage ads for this categories.
A segment is a fraction of audience outlined based on certain parameters: e.g. gender, age or any
other parameters known to the app and passed to Appodeal SDK. Additional ad management settings can
be applied to each segment. Read more on segments in our [FAQ](/advanced/segments).
Once user segments have been created, they can then be analyzed and used to configure ads.
To create a new segment go [here](https://app.appodeal.com/v3/segments).
:::info
If you have no segments, all users will be assigned to default segment.
If you have multiple segments, their order is important. Only the first segment related to the given user will apply.
All of the rest will be ignored.
:::
-------------
### Manual Filters
Manual Filters allow to group users by any available metric. E.g. you know the sources that directed
users to your app and you want to track the statistics for such sources — create a segment for each
source and mark each user with the source they came from.
To create such a segment, you have to set its name and value:
```kotlin showLineNumbers
Appodeal.setCustomFilter(name: String, value: Any?)
```
```java showLineNumbers
Appodeal.setCustomFilter(@NonNull String name, @Nullable Object value);
```
Value can be boolean, numeric or string-based.
Example:
```kotlin showLineNumbers
Appodeal.setCustomFilter(name = "levels_played", value = 3)
```
```java showLineNumbers
Appodeal.setCustomFilter("levels_played", 3);
```
------------------
### Bought In-Apps and In-Apps Amount Filters
**Bought In-Apps** allows to group users by the fact of purchasing
in-apps. This will help you adjust the ads for such users or turn them
off if needed.
**In-Apps Amount** filter allows you to group users who've made a particular amount of in-app purchases.
Please submit the purchase info via Appodeal SDK to make these settings work correctly.
```kotlin showLineNumbers
Appodeal.trackInAppPurchase(context = context, amount = 5.0, currency = "USD")
```
```java showLineNumbers
Appodeal.trackInAppPurchase(context, 5.0, "USD");
```
------------------
If you have no segments, all users will be assigned to default segment.
If you have multiple segments, their order is important. Only the first segment related to the given
user will apply. All of the rest will be ignored.
## Placements
Appodeal SDK allows you to tag each impression with different placement. Read more on placements in
our [FAQ](/advanced/placements).
To show an ad with placement, you have to call show method like this:
```kotlin showLineNumbers
// for Interstitial, Rewarded Video, Banner and MREC
Appodeal.show(activity = activity, adTypes = adTypes, placementName = "placement")
// for Native Ad
val nativeAd: NativeAd = ...
val nativeAdView: NativeAdView = ...
nativeAdView.registerView(nativeAd = nativeAd, placementName = "placement")
```
```java showLineNumbers
// for Interstitial, Rewarded Video, Banner and MREC
Appodeal.show(activity, adTypes, "placement");
// for Native Ad
NativeAd nativeAd = ...
NativeAdView nativeAdView = ...
nativeAdView.registerView(nativeAd, "placement");
```
To check if an impression is available for a given placement, use:
```kotlin showLineNumbers
// for Interstitial, Rewarded Video, Banner and MREC
Appodeal.canShow(adTypes = adTypes, placementName = "placement")
// for Native Ad
nativeAd.canShow(placementName = "placement")
```
```java showLineNumbers
// for Interstitial, Rewarded Video, Banner and MREC
Appodeal.canShow(adTypes, "placement");
// for Native Ad
nativeAd.canShow("placement");
```
You can configure your impression logic for each placement.
If you have no placements or call showAd with placement that does not exist, the impression will be
tagged with 'default' placement with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
---
## Self-Hosted Bidon
Configuring and retrieving the Bidon endpoint.
:::info
Bidon documentation can be found [here](https://docs.bidon.org/).
:::
### Set Bidon Endpoint
To set a custom Bidon endpoint, use the following method:
```kotlin showLineNumbers
Appodeal.setBidonEndpoint("https://example.com/api")
```
```java showLineNumbers
Appodeal.setBidonEndpoint("https://example.com/api");
```
:::info Should be called before the SDK initialization.
:::
-------------
### Get Bidon Endpoint
To retrieve the currently set Bidon endpoint, use the following method:
```kotlin showLineNumbers
Appodeal.getBidonEndpoint()
```
```java showLineNumbers
Appodeal.getBidonEndpoint();
```
-------------
---
## Testing
After adding a new app to Appodeal and integrating the SDK, we recommend testing your app.
Here are the tips for successful testing.
## Integration Review
### Step 1. Prepare Settings On Appodeal Side
#### Check Mediation Settings
Go to Application Settings → Mediation Settings → Line Items.
Choose the ad type you are interested in and check the network connection.
In the Line Items section, you can see the rules for automatically
connecting ad networks. Once you fulfill all the requirements, networks
will be connected automatically using the default Appodeal account.
**Example:**
For new applications, a few networks will be connected by default if the Appodeal server receives a request for a
certain ad type.
If you see `This network will be activated by ad request.`
Try to [request real ads](#check-sdk-integration-with-real-ads) to activate this network using the default Appodeal
account.
:::info
Make sure you have **at least 2-3 enabled** networks. If the
requirements for automatic network connection are not fulfilled, link a
personal account using [Networks Setup](../../../networks-setup/introduction) to have more networks connected.
:::
Make sure the ad units are enabled for the connected networks:
#### Check Priorities (Waterfall Configuration).
Go to Application settings → Mediation Settings → Priorities, and
choose the ad type.
By default, only default priority configuration is enabled for the
waterfall, where all ad units from connected networks are placed. Make
sure line items have been added to your current configuration.
If not, add them to the configuration by dragging and dropping ad units
from the Unused Line Items list on the left to Automatic Priority.
### Step 2. Test Your SDK Integration
#### Check SDK Integration With Test Ads.
:::info
Test mode ads have a 100% fill rate, they load almost instantly compared to real ads,
which can take some time to load (0-30 seconds depending on the ad type).
:::
1. [Enable Test Mode](#enable-test-mode)
2. [Enable SDK Logging](#enable-logging)
3. Make sure that all necessary adapters have been integrated into the
app. To get test ads, it's required to have all adapters marked by a
star in [Mediation Wizard](../get-started).
4. Run the app and go to all placements where you added ads. Make sure
they are loaded and shown successfully.
5. Open the logs tab and check Appodeal SDK logs. For more information,
look through the [SDK logging](#enable-logging)
:::info
Requests for test ads are not counted as real requests, however,
Appodeal needs at least one real request for automatically activating
networks for a certain ad type.
:::
#### Check SDK Integration With Real Ads.
We recommend testing apps using test mode to ensure proper performance
with real ads. However, it's necessary to make sure SDK integration is
correct and all networks are ready to use.
1. Disable test mode by commenting out the method you used to enable
it.
2. Check that Appodeal SDK [logging](#enable-logging) is enabled.
3. Make sure that all necessary adapters for the networks you are
planning to use have been integrated. For more information please
visit the [Mediation Wizard](../get-started).
4. Open your application and initialize SDK to make a request for
activating the ad networks. You can see all the activity of our SDK
in the logs under the "Appodeal" tag.
5. When network setup is ready, run the app again and open the logs
console. Make sure there are no errors in the logs. Use [SDK logging](#enable-logging)
to analyze Appodeal logs. Go through all placements where you added
ads. Make sure they are loaded and shown successfully with no
exceptions and errors
:::info
If your app is not published in one of the supported app stores (Google
Play, App Store, Amazon), the number of impressions for live ads is
restricted to [2,000](/faq-and-troubleshooting/faq/ad-mediation/traffic-limit).
:::
## Useful SDK Methods
### Enable Test Mode
Using test mode allows you to get our test ad creatives with 100% fillrate.
```kotlin showLineNumbers
Appodeal.setTesting(testMode = true)
```
```java showLineNumbers
Appodeal.setTesting(true)
```
:::info Should be called before the SDK initialization.
:::
------------------
### Enable Logging
SDK logging allows you to check SDK integration and activity, including information about waterfalls
with ad units, ads requesting, loading, and some other. We recommend always enabling logs and use
the verbose log level to get full SDK information.
To enable debug logging, use the code below:
```kotlin showLineNumbers
Appodeal.setLogLevel(logLevel = Log.LogLevel.verbose)
```
```java showLineNumbers
Appodeal.setLogLevel(Log.LogLevel.verbose);
```
Logs will be written to logcat using the `Appodeal` tag.
Available parameters:
- `Log.LogLevel.none` - logs off;
- `Log.LogLevel.debug` - debug messages;
- `Log.LogLevel.verbose` - all SDK and ad network messages.
Connect a device with the app installed, open the Android Studio Logcat console, run the app and check
SDK logs by the Appodeal tag. For more information about the console please visit
[**Debugging with Android Studio**](https://developer.android.com/studio/debug/logcat).
------------------
### Disable Networks
:::info Should be called before the SDK initialization.
:::
```kotlin showLineNumbers
Appodeal.disableNetwork(network = "network_name")
```
:::info You can find all available network names in `com.appodeal.ads.adnetworks.MediationAdNetwork`.
```kotlin showLineNumbers
MediationAdNetwork.A4g.networkName
MediationAdNetwork.AdColony.networkName
MediationAdNetwork.Admob.networkName
MediationAdNetwork.AdmobNative.networkName
MediationAdNetwork.AdmobMediation.networkName
MediationAdNetwork.Applovin.networkName
MediationAdNetwork.Appodeal.networkName
MediationAdNetwork.BidMachine.networkName
MediationAdNetwork.BigoAds.networkName
MediationAdNetwork.DTExchange.networkName
MediationAdNetwork.Gam.networkName
MediationAdNetwork.Meta.networkName
MediationAdNetwork.Mintegral.networkName
MediationAdNetwork.Inmobi.networkName
MediationAdNetwork.IronSource.networkName
MediationAdNetwork.Mraid.networkName
MediationAdNetwork.MyTarget.networkName
MediationAdNetwork.Notsy.networkName
MediationAdNetwork.UnityAds.networkName
MediationAdNetwork.Vast.networkName
MediationAdNetwork.Vungle.networkName
MediationAdNetwork.Bidon.networkName
MediationAdNetwork.Yandex.networkName
```
:::
```java showLineNumbers
Appodeal.disableNetwork("network_name");
```
:::info You can find all available network names in `com.appodeal.ads.adnetworks.MediationAdNetwork`.
```java showLineNumbers
MediationAdNetwork.A4g.getNetworkName();
MediationAdNetwork.AdColony.getNetworkName();
MediationAdNetwork.Admob.getNetworkName();
MediationAdNetwork.AdmobNative.getNetworkName();
MediationAdNetwork.AdmobMediation.getNetworkName();
MediationAdNetwork.Applovin.getNetworkName();
MediationAdNetwork.Appodeal.getNetworkName();
MediationAdNetwork.BidMachine.getNetworkName();
MediationAdNetwork.BigoAds.getNetworkName();
MediationAdNetwork.DTExchange.getNetworkName();
MediationAdNetwork.Gam.getNetworkName();
MediationAdNetwork.Meta.getNetworkName();
MediationAdNetwork.Mintegral.getNetworkName();
MediationAdNetwork.Inmobi.getNetworkName();
MediationAdNetwork.IronSource.getNetworkName();
MediationAdNetwork.Mraid.getNetworkName();
MediationAdNetwork.MyTarget.getNetworkName();
MediationAdNetwork.Notsy.getNetworkName();
MediationAdNetwork.UnityAds.getNetworkName();
MediationAdNetwork.Vast.getNetworkName();
MediationAdNetwork.Vungle.getNetworkName();
MediationAdNetwork.Bidon.getNetworkName();
MediationAdNetwork.Yandex.getNetworkName();
```
:::
------------------
### Disable Networks For Specific Ad Types
:::info Should be called before the SDK initialization.
:::
```kotlin showLineNumbers
Appodeal.disableNetwork(network = "network_name", adTypes = adTypes)
```
:::info You can find all available network names in `com.appodeal.ads.adnetworks.MediationAdNetwork`.
```kotlin showLineNumbers
MediationAdNetwork.A4g.networkName
MediationAdNetwork.AdColony.networkName
MediationAdNetwork.Admob.networkName
MediationAdNetwork.AdmobNative.networkName
MediationAdNetwork.AdmobMediation.networkName
MediationAdNetwork.Applovin.networkName
MediationAdNetwork.Appodeal.networkName
MediationAdNetwork.BidMachine.networkName
MediationAdNetwork.BigoAds.networkName
MediationAdNetwork.DTExchange.networkName
MediationAdNetwork.Gam.networkName
MediationAdNetwork.Meta.networkName
MediationAdNetwork.Mintegral.networkName
MediationAdNetwork.Inmobi.networkName
MediationAdNetwork.IronSource.networkName
MediationAdNetwork.Mraid.networkName
MediationAdNetwork.MyTarget.networkName
MediationAdNetwork.Notsy.networkName
MediationAdNetwork.UnityAds.networkName
MediationAdNetwork.Vast.networkName
MediationAdNetwork.Vungle.networkName
MediationAdNetwork.Bidon.networkName
MediationAdNetwork.Yandex.networkName
```
:::
```java showLineNumbers
Appodeal.disableNetwork("network_name", adTypes);
```
:::info You can find all available network names in `com.appodeal.ads.adnetworks.MediationAdNetwork`.
```java showLineNumbers
MediationAdNetwork.A4g.getNetworkName();
MediationAdNetwork.AdColony.getNetworkName();
MediationAdNetwork.Admob.getNetworkName();
MediationAdNetwork.AdmobNative.getNetworkName();
MediationAdNetwork.AdmobMediation.getNetworkName();
MediationAdNetwork.Applovin.getNetworkName();
MediationAdNetwork.Appodeal.getNetworkName();
MediationAdNetwork.BidMachine.getNetworkName();
MediationAdNetwork.BigoAds.getNetworkName();
MediationAdNetwork.DTExchange.getNetworkName();
MediationAdNetwork.Gam.getNetworkName();
MediationAdNetwork.Meta.getNetworkName();
MediationAdNetwork.Mintegral.getNetworkName();
MediationAdNetwork.Inmobi.getNetworkName();
MediationAdNetwork.IronSource.getNetworkName();
MediationAdNetwork.Mraid.getNetworkName();
MediationAdNetwork.MyTarget.getNetworkName();
MediationAdNetwork.Notsy.getNetworkName();
MediationAdNetwork.UnityAds.getNetworkName();
MediationAdNetwork.Vast.getNetworkName();
MediationAdNetwork.Vungle.getNetworkName();
MediationAdNetwork.Bidon.getNetworkName();
MediationAdNetwork.Yandex.getNetworkName();
```
:::
-------
### Test Third-Party Network Adapters Integration
You can use our Test Activity for manual testing adapters integration.
To start Test Activity call:
```kotlin showLineNumbers
Appodeal.startTestActivity(activity = activity)
```
```java showLineNumbers
Appodeal.startTestActivity(activity);
```
---
## User Data
Our SDK provides user data tranfer for better ad targeting and higher eCPM. All parameters are optional.
## Set User Id
To assign an ID to a user, please call this method before Appodeal initialization:
```kotlin showLineNumbers
Appodeal.setUserId(userId = "user_id")
```
```java showLineNumbers
Appodeal.setUserId("user_id");
```
------------------
:::caution
For data privacy and GDPR-compliance reasons, you may NOT use email address, phone number, real name or any other
personally identifiable information in the user ID you set with this call.
:::
## Custom Segment Matching
If the logic of your application allows specifying user's
characteristics, then you can pass specific parameters to the Appodeal
SDK. You can
use [Segments](/advanced/segments) in the future.
```kotlin showLineNumbers
Appodeal.setCustomFilter(name: String, value: Any?)
```
```java showLineNumbers
Appodeal.setCustomFilter(@NonNull String name, @Nullable Object value);
```
------------------
## Send Extra Data
You can send key-value data to Appodeal.
```kotlin showLineNumbers
Appodeal.setExtraData(name: String, value: Any?)
```
```java showLineNumbers
Appodeal.setExtraData(@NonNull String name, @Nullable Object value);
```
:::tip
To send the device identifier from a mobile attribution service and match it with Appodeal user id,
use `attribution_id` as a key and a unique identifier from your attribution service as a value and
if you use this method for attribution call it **before Appodeal SDK initialization**.
:::
---
## Get Started
| Release Version | Release Date |
|-----------------|--------------|
| { getReleaseVersion("ios") } | { getReleaseDate("ios") } |
Follow this guide to get the best out of Appodeal.
The Appodeal SDK gives you **access to 70+ Ad Demand Sources and makes them compete
against each other in a real-time auction**, maximizing your ad revenues.
The Appodeal SDK also provides _In-app Bidding, Automatic UA Optimization,
User Segmentation & A/B Testing, Cross-Promotion and Direct Deals, Instant Payouts_,
and [much](https://appodeal.com/monetization/) [more](/faq-and-troubleshooting/faq/ad-mediation/getting-started-with-ad-mediation).
:::info
Appodeal SDK provides **two** ways of integration. From the options below, choose the one that fits your needs better:
:::
**The Appodeal SDK Full Package** - The Appodeal SDK provides you with tools to grow your mobile apps and games.
In addition to the monetization services, you can benefit from UA (User Acquisition)
and in-app analytics services. Here is the list of services Appodeal SDK Full Package includes:
- [Get started with Appodeal](get-started) to gain access to **Monetization** and **Analytics**.
- Connect with [Adjust](./services/adjust) or [AppsFlyer](./services/appsflyer) to unlock **Attribution features**.
- Connect with [Meta](./services/meta) (_formerly known as facebook-core)_ for **User Acquisition**.
- Connect with [Firebase](./services/firebase) for **Analytics** + remote config for **product A/B tests** and settings.
If you plan to run UA campaigns, want to analyze your metrics in our Appodeal's business intelligence tool without using MMP,
or want to use remote config for tests and settings, your option is - **The Appodeal SDK Full Package**.
**The Appodeal SDK Mediation only** - If you do not plan to run (_UA_) User Acquisition campaigns,
nor want to use Appodeal advanced analytics, we have created a lite version of our SDK, only with mediation.
During the integration, you will not be required to install any additional services apart from mediation.
This may speed up your integration process, and you can always upgrade to the Full Package whenever you're ready.
-----------------
:::tip
Please follow this integration guide step by step and choose your integration option when needed.
:::
The following document shows how to integrate Appodeal in your iOS project with your desired networks
via CocoaPods and manual setup, and configure all your ad formats.
:::info Minimum requirements:
- iOS 13.0 and above for Firebase, IronSource, LevelPlay, DTExchange, Unity, and Yandex. iOS 12.4 for MyTarget.
In all other cases, iOS 12.0 is sufficient.
- Xcode 16.4 or higher.
:::
You can use our **demo app** as a reference project.
## Step 1. Import SDK
Fat and CocoaPods SDK versions work in pure Obj-C, and Swift as well as mixed Obj-C or Swift projects.
:::info Objective C
If your project is a **pure Objective-C project**, you should **add an empty Swift file**, for example, `Dummy.swift`.
Xcode will offer you to create Bridging Header after adding the empty Swift file, press "**Create"**.
:::
Please select the ad types that are used in your application and the ad networks that you want to include.
:::info Disabling ad networks
As you have selected **Appodeal SDK Full Package** option, make sure you have not excluded services from the Podfile or from the SDK package.
We recommend using the standard configuration, as disabling some of the recommended adapters can negatively affect revenue.
:::
If you want to use **Appodeal as a mediation only**, you need to **exclude** services from the Podfile or from the SDK package.
Please select the ad types that are used in your application and the ad networks that you want to include, make sure services are **excluded**.
:::info Disabling ad networks
We recommend using the standard configuration, as disabling some of the recommended adapters can negatively affect revenue.
:::
1. Podfile configuration
:::caution
CocoaPods 1.12.0 or higher is required. To get information about CocoaPods update, please
see this [documentation](https://guides.cocoapods.org/using/getting-started.html).
:::
:::info Configuring Podfile
Here is a base and recommended `Podfile` sample code.
You can add or remove any ad network or service adapter depending on your requirements.
We provide a convenient and interactive way to customize and generate `Podfile` code based on selected ad types, networks and services.
To configure your `Podfile` please visit [Configure Mediated Networks](advanced/configure-mediated-networks) page.
:::
:::info iOS 18 support
**Appodeal {getReleaseVersion("ios")}** is fully compatible with iOS 18. If you have implemented **Appodeal** and faced a problem while
publishing your app to the AppStore please update to **Appodeal {getReleaseVersion("ios")}** version. If there is no possibility for update
to fix the issue just remove `APDSmaatoAdapter` and `BidMachineSmaatoAdapter` from your podfile or use the post-install hook provided below.
:::
```bash
post_install do |installer|
xcframework_path = "#{installer.sandbox.root}/smaato-ios-sdk/vendor"
Dir.glob("#{xcframework_path}/**/*.framework/OMSDK_Smaato").each do |binary|
if File.exist?(binary)
puts "Stripping bitcode from: #{binary}"
system("xcrun bitcode_strip #{binary} -r -o #{binary}")
end
end
end
```
2. Call pod install
Call `$ pod install` to install CocoaPods dependencies or `$ pod update` to update.
If you do not have an installed pod, [Install CocoaPods](http://guides.cocoapods.org/using/getting-started.html)
to simplify dependency management
```bash
sudo gem install cocoapods
```
If you have problems with pods versions, please run the following code:
```bash
rm -rf "${HOME}/Library/Caches/CocoaPods"
rm -rf "`pwd`/Pods/"
pod update
```
If the official repo doesn't respond, you can add lines to your podspec and update pods from the Appodeal mirror repository:
```ruby
source 'https://github.com/appodeal/CocoaPods.git'
source 'https://cdn.cocoapods.org/'
```
If you are receiving error like this
`[!] CDN: trunk URL couldn't be downloaded: https://cdn.cocoapods.org/Specs/0/3/e/Appodeal/${VERSION}/Appodeal.podspec.json Response: Failure when receiving data from the peer`
please run the following command
```bash
pod repo remove trunk
```
3. Open your project using .xcworkspace file from now on.
1. Download SDK
Integrate iOS SDK **{ getReleaseVersion("ios") }**
2. Copy files to your project
Open your project in Xcode, then drag the downloaded frameworks
into your project. (use the "Product Navigator view").
Choose "**Copy items into destination group's folder**" and click "**Finish**".
3. Add Linker flag
Now you need to add the -ObjC linker flag. In your XCode project click
**Project** -> **Build Settings** -> Search for "**Other Linker Flags**" -> Add `-ObjC`.
Add libraries to **Build Phases** -> **Link Binary with Libraries** to ensure compatibility with Objective-C categories and low-level system functions:
+ `libc++.tbd`
+ `libresolv.tbd`
+ `libz.tbd`
+ `libc++abi.tbd`
+ `libbz2.tbd`
4. Set Embed and Sign for Frameworks
In your Xcode project, go to **Project** -> **General** -> **Frameworks, Libraries, and Embedded Content** section. Ensure the following frameworks are set to `Embed & Sign`:
+ `ATOM.xcframework`
+ `AdjustSigSdk.xcframework`
+ `AppLovinSDK.xcframework`
+ `DTBiOSSDK.xcframework`
+ `FBAEMKit.xcframework`
+ `FBSDKCoreKit.xcframework`
+ `FBSDKCoreKit_Basics.xcframework`
+ `InMobiSDK.xcframework`
+ `MobileFuseSDK.xcframework`
+ `MolocoSDK.xcframework`
+ `OMSDK_Ogury.xcframework`
+ `OMSDK_Pubmatic.xcframework`
+ `OMSDK_Pubnativenet.xcframework`
+ `OMSDK_Smaato.xcframework`
+ `OguryAds.xcframework`
+ `OguryCore.xcframework`
+ `OgurySdk.xcframework`
+ `OpenWrapSDK.xcframework`
+ `SmaatoSDKBanner.xcframework`
+ `SmaatoSDKCore.xcframework`
+ `SmaatoSDKInAppBidding.xcframework`
+ `SmaatoSDKInterstitial.xcframework`
+ `SmaatoSDKNative.xcframework`
+ `SmaatoSDKOpenMeasurement.xcframework`
+ `SmaatoSDKOutstream.xcframework`
+ `SmaatoSDKRewardedAds.xcframework`
+ `SmaatoSDKRichMedia.xcframework`
+ `SmaatoSDKVideo.xcframework`
+ `StartApp.xcframework`
+ `TaurusxAdsSDK.xcframework`
---
## Step 2. Prepare Your Application
### Add SKAdNetworkIds
:::important
To ensure SKAdNetwork packages can be correctly scanned, follow the [Semantic Versioning (SemVer) format](../advanced/app-version-format) for your app version.
:::
Ad networks used in Appodeal mediation support conversion tracking using Apple's `SKAdNetwork`,
which means ad networks are able to attribute an app install even when IDFA is unavailable.
To enable this functionality, you will need to update the `SKAdNetworkItems` key with an additional
dictionary in your `Info.plist`.
:::info
If you are using Xcode 14+ and SwiftUI in your app,
then you can notice that there is no Info.plist
in project navigator by default.
You need to go to Target → Info tab → Custom iOS Target Properties and
make any changes to it by simply adding an empty line so
the Info.plist file shows up in your project navigator.
:::
1. Select **Info.plist** in the Project navigator in Xcode
2. Right-click on **Info.plist** file → Open as → Source Code
3. Copy the **SKAdNetworkItems** from below and paste it into your **Info.plist** file
There is SKAdNetworks IDs in Info.plist format
### Add AdAttributionKit IDs
[AdAttributionKit](https://developer.apple.com/documentation/AdAttributionKit) is Apple's modern framework for ad attribution that works alongside `SKAdNetwork`.
Ad networks used in Appodeal mediation support `AdAttributionKit` for improved conversion tracking and attribution on iOS 17.4+.
To enable this functionality, you will need to add the `AdAttributionKitItems` key with an additional
dictionary in your `Info.plist`, similar to `SKAdNetworkItems`.
1. Select **Info.plist** in the Project navigator in Xcode
2. Right-click on **Info.plist** file → Open as → Source Code
3. Copy the **AdAttributionKitItems** from below and paste it into your **Info.plist** file
There is AdAttributionKit IDs in Info.plist format
### Configure App Transport Security Settings
In order to serve ads, the SDK requires you to allow arbitrary loads. Set up the following keys in **Info.plist** of your app:
1. Go to your **Info.plist** file, then press Add+ anywhere in the first column of the key list.
2. Add **App Transport Security Settings** key and set its type to **Dictionary** in the second column.
3. Press **Add+** at the end of the name **App Transport Security Settings key** and choose
**Allow Arbitrary loads**. Set its type to **Boolean** and its value to **Yes**.
You can also add the key to your Info.plist directly, using this code:
```xml showLineNumbers
NSAppTransportSecurityNSAllowsArbitraryLoads
```
### Location Description (Optional)
**NSLocationWhenInUseUsageDescription** - Entry is required if your application allows Appodeal SDK to use location data.
```xml showLineNumbers
NSLocationWhenInUseUsageDescription needs your location for analytics and advertising purposes
```
:::info
To use location services, your app requests authorization,
and the system prompts the user to grant or deny the request.
Please implement Core Location and ask users permission
according to the official [documentation](https://developer.apple.com/documentation/corelocation/requesting_authorization_to_use_location_services).
:::
### Other Feature Usage Descriptions
:::warning Important
Admob Bidding is now available with **Appodeal SDK 3.2.0**.
Don't forget to download our newest version of Admob Sync tool from this [page](https://amsa-updates.appodeal.com/) and perform sync.
You can read more about Admob Sync in our [guide](/networks-setup/ad-networks/network-connection/admob-sync).
:::
To improve ad performance the following entries should be added:
1. **GADApplicationIdentifier** - When including AdMob in your project,
you must also add your AdMob app ID to your **info.plist**.
Use the key `GADApplicationIdentifier` with the value being your AdMob app ID.
For more information about Admob sync check out our [Admob guide](/networks-setup/ad-networks/network-connection/admob).
2. **NSUserTrackingUsageDescription** - Starting from iOS 14, using IDFA requires
permission from the user. The following entry must be added in order to improve ad performance.
3. **NSCalendarsUsageDescription** - Recommended by some ad networks.
```xml
GADApplicationIdentifierYOUR_ADMOB_APP_IDNSUserTrackingUsageDescription needs your advertising identifier to provide personalised advertising experience tailored to youNSCalendarsUsageDescription needs your calendar to provide personalised advertising experience tailored to you
```
:::info
Please note, if you have removed the Admob adapter,
don't forget to remove the **BDMNotsyAdapter** as well using this [guide](/faq-and-troubleshooting/troubleshooting/ios-common-issues/gadinvalidinitializationexception#GADInvalidInitializationExceptioniOS-RemoveAdmobAdapterFromYourApp).
:::
## Step 3. Initialize SDK
Before initialisation, we highly recommend to receive
all required permissions from the user.
Please follow this [Data Protection guide](data-protection/gdpr-and-ccpa).
Import **Appodeal** into **AppDelegate (AppDelegate.m)** and initialize the SDK:
```swift showLineNumbers
```
```objc showLineNumbers
#import
```
We recommended to call initialization method
in **AppDelegate** `-didFinishLaunchingWithOptions:` function:
```swift showLineNumbers
@UIApplicationMain
final class MyAppDelegate: UIResponder, UIApplicationDelegate, AppodealInitializationDelegate {
func application(
_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
Appodeal.setAutocache(false, types: .interstitial)
Appodeal.setLogLevel(.verbose)
// Optional delegate for initialization completion
Appodeal.setInitializationDelegate(self)
/// Any other pre-initialization
/// app specific logic
// highlight-start
Appodeal.initialize(
withApiKey: "APP_KEY",
types: .interstitial
)
// highlight-end
return true
}
func appodealSDKDidInitialize() {
// Appodeal SDK did complete initialization
}
}
```
```objc showLineNumbers
@interface MyAppDelegate ()
@end
@implementation MyAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Appodeal setAutocache:NO types:AppodealAdTypeInterstitial]; [Appodeal setLogLevel:APDLogLevelVerbose];
// Optional delegate for initialization completion
[Appodeal setInitializationDelegate:self];
/// Any other pre-initialization
/// app specific logic
// highlight-start
[Appodeal initializeWithApiKey:@"APP KEY" types:AppodealAdTypeInterstitial];
// highlight-end
return YES;
}
- (void)appodealSDKDidInitialize {
// Appodeal SDK did complete initialization
}
@end
```
`adTypes` is a parameter responsible for ad formats (ex. `AppodealAdTypeRewardedVideo, AppodealAdTypeInterstitial`).
Initialize only those ad types you want to use in your app to avoid getting ad requests to unused ones.
`consent` is an object responsible for agreement to having user's personal data collected according to GDPR and CCPA laws.
You can find more information [here](data-protection/gdpr-and-ccpa).
Make sure to replace `YOUR_APP_KEY` with the actual app key.
You can find it in the list of applications in your [personal account](https://app.appodeal.com/apps).
## Step 4. Configure Ad Types
Appodeal SDK is now imported and you're ready to implement an ad.
Appodeal offers a number of different ad formats, so you can choose the one that best fits your app's user experience.
## Step 5. What's next
### Configure App Privacy
Configure App Privacy according to this doc:
### Add App-ads.txt File
The app-ads.txt file is a text file which provides a mechanism for publishers to declare their authorized digital sellers.
You can find detailed information [here](../../advanced/app-ads)
---
## Banner(Ad-types)
Banner ads are classic static banners, usually located at the bottom or
top of the screen. Appodeal supports traditional 320x50 banners, 728x90
tablet banners and smart banners that adjust to the size and orientation
of the device.
You can use our **demo app** as a reference project.
## Fixed Positioned Banner
### Display Banner At The Bottom Of The Screen
Banner is a singleton now, if you are using `bannerTop` or `bannerBottom` on different controllers, the SDK will use the same banner instance.
Banner ads are refreshed every 15 seconds automatically by default. To display a banner, you need to call the following code:
```swift showLineNumbers
Appodeal.showAd(.bannerBottom, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleBannerBottom rootViewController:self];
```
---
### Display Banner At The Top Of The Screen
```swift showLineNumbers
Appodeal.showAd(.bannerTop, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleBannerTop rootViewController:self];
```
---
### Display Banner At The Left Or Right Corner Of The Screen
If your app uses landscape interface orientation you can show Appodeal Banner at the left or right corner.
The banner will have the offset according to the safe area layout guide.
:::caution
Disable banner smart sizing if you use AppodealShowStyleBannerLeft
or AppodealShowStyleBannerRight
:::
```swift showLineNumbers
// Overrides default rotation angles
// Appodeal.setBannerLeftRotationAngleDegrees(90, rightRotationAngleDegrees: 180)
Appodeal.showAd(.bannerLeft, forPlacement: placement, rootViewController: self)
// Appodeal.showAd(.bannerRight, forPlacement: placement, rootViewController: self)
```
```objc showLineNumbers
// Overrides default rotation angles
// [Appodeal setBannerLeftRotationAngleDegrees:90 rightRotationAngleDegrees:180];
[Appodeal showAd: AppodealShowStyleBannerLeft forPlacement: placement rootViewController: self];
// [Appodeal showAd: AppodealShowStyleBannerRight forPlacement: placement rootViewController: self];
```
---
### Checking If Ad Is Loaded
You can check if the ad has been loaded before showing it. This method
returns a boolean value indicating whether or not the banner has been
loaded.
```swift showLineNumbers
Appodeal.isReadyForShow(with: .bannerTop)
```
```objc showLineNumbers
[Appodeal isReadyForShowWithStyle: AppodealShowStyleBannerTop];
```
---
We recommend you to check ad caching before trying to show it.
### Callbacks
Callbacks are used to track different lifecycle events of an ad, e.g.,
when a banner has successfully loaded or is about to appear. To get
them, you need to set the delegate as follows:
```swift showLineNumbers
//set delegate
Appodeal.setBannerDelegate(self)
```
```objc showLineNumbers
//set delegate
[Appodeal setBannerDelegate:self];
```
Usually, the class that implements banners is also the delegate class.
That's why the delegate property can be set to `self`.
Now you can use the following callback methods:
```swift showLineNumbers
// banner was loaded (precache flag shows if the loaded ad is precache)
func bannerDidLoadAdIsPrecache(_ precache: Bool) {}
// banner was shown
func bannerDidShow() {}
// banner failed to load
func bannerDidFailToLoadAd() {}
// banner was clicked
func bannerDidClick() {}
// banner did expire and could not be shown
func bannerDidExpired() {}
```
```objc showLineNumbers
// banner was loaded (precache flag shows if the loaded ad is precache)
- (void)bannerDidLoadAdIsPrecache:(BOOL)precache {}
// banner was shown
- (void)bannerDidShow {}
// banner failed to load
- (void)bannerDidFailToLoadAd {}
// banner was clicked
- (void)bannerDidClick {}
// banner did expire and could not be shown
- (void)bannerDidExpired {}
```
:::tip
All callbacks are called on the main thread.
:::
:::caution
If automatic caching is ON for the Banner ad type, do not show the
banner in the `bannerDidLoadAdIsPrecache` callback. The banner will be
refreshed automatically after the first show.
:::
### Hide
To remove the banner from your view hierarchy:
```swift showLineNumbers
Appodeal.hideBanner()
```
```objc showLineNumbers
[Appodeal hideBanner];
```
---
## Custom Positioned Banner
### Display Banner In Programmatically Created View
You can also add the Appodeal banner to your view hierarchy manually.
For example:
```swift showLineNumbers
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let banner = Appodeal.banner() {
self.view.addSubview(banner)
banner.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 50)
}
}
```
```objc showLineNumbers
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.view addSubview:[Appodeal banner]];
[Appodeal banner].frame = CGRectMake(0, 0, self.view.bounds.size.width, 50);
}
```
---
:::caution Important!
Custom `BannerView` must be on the top of the hierarchy and mustn't be
overlapped by other views.
:::
## Advanced
### Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```swift showLineNumbers
Appodeal.showAd(.bannerTop, forPlacement: placement, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleBannerTop forPlacement:placement rootViewController:self];
```
---
If the loaded ad can't be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement) check if the ad can be shown
before calling show method:
```swift showLineNumbers
Appodeal.canShow(.bannerTop, forPlacement: placement)
```
```objc showLineNumbers
[Appodeal canShow:AppodealAdTypeBannerTop forPlacement:placement];
```
---
You can configure your impression logic for each placement.
If you have no placements or call Appodeal.show with a placement that
does not exist, the impression will be tagged with 'default' placement
with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
### Check If Banner Is Initialized
```swift showLineNumbers
Appodeal.isInitialized(for: .banner)
```
```objc showLineNumbers
[Appodeal isInitalizedForAdType: AppodealAdTypeBanner];
```
---
Returns `true` if banner was initialized.
### Check If Autocache Is Enabled
```swift showLineNumbers
Appodeal.isAutocacheEnabled(.banner)
```
```objc showLineNumbers
[Appodeal isAutocacheEnabled: AppodealAdTypeBanner];
```
---
Returns `true` if autocache is enabled for banner.
### Advanced BannerView Integration
If basic integration is not appropriate for you due to the complex views
hierarchy of your app, you can use `AppodealBannerView UIView` subclass
to integrate banners.
```swift showLineNumbers
class YourViewController : UIViewController, AppodealBannerViewDelegate {
override func viewDidLoad () {
super.viewDidLoad()
// required: init ad banner
var bannerView: AppodealBannerView!
bannerView.init(size: bannerSize, rootViewController: self);
// optional: set delegate
bannerView.setDelegate(self);
// required: add banner to superview and call -loadAd to start banner loading
self.view addSubview(bannerView);
bannerView.loadAd();
}
// optional: implement any of AppodealBannerViewDelegate methods
func bannerViewDidLoadAd(_ bannerView: APDBannerView, isPrecache precache: Bool) {
NSLog("bannerView was loaded")
}
func bannerView(_ bannerView: APDBannerView, didFailToLoadAdWithError error: Error) {
NSLog("bannerView failed to load");
}
func bannerViewDidInteract(_ bannerView: APDBannerView) {
NSLog("bannerView was clicked")
}
func bannerViewDidShow(_ bannerView: APDBannerView) {
NSLog("bannerView was shown")
}
func bannerViewExpired(_ bannerView: APDBannerView) {
NSLog("bannerView did expire and could not be shown")
}
}
```
```objc showLineNumbers
#import "YourViewController.h"
#import
@interface YourViewController ()
@end
@implementation YourViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// required: init ad banner
AppodealBannerView *bannerView = [[AppodealBannerView alloc] initWithSize:kAppodealUnitSize_320x50 rootViewController:self];
// optional: set delegate
bannerView.delegate = self;
// required: add banner to superview and call -loadAd to start banner loading
[self.view addSubview:bannerView];
[bannerView loadAd];
}
// optional: implement any of AppodealBannerViewDelegate methods
- (void)bannerViewDidLoadAd:(APDBannerView *)bannerView isPrecache:(BOOL)precache{
NSLog(@"Banner %@ did load!", bannerView);
}
- (void)bannerViewDidInteract:(APDBannerView *)bannerView {
NSLog(@"Banner %@ did interact", bannerView);
}
- (void)bannerView:(APDBannerView *)bannerView didFailToLoadAdWithError:(NSError *)error {
NSLog(@"Banner %@ did fail with error: %@", bannerView, error);
}
- (void)bannerViewDidShow:(APDBannerView *)bannerView {
NSLog(@"Banner %@ did show ad ", bannerView);
}
- (void)bannerViewExpired:(APDBannerView *)bannerView {
NSLog(@"Banner %@ expired", bannerView);
}
@end
```
:::tip
To provide better user expirience we recommend to use single banner instance
for all you screens where banner is shown.
:::
There are several examples how to properly pass banner instance
between screens using `UIStoryboardSegue` and
in-code navigation through `UINavigationController`.
#### Storyboard based application example.
1. Create auxiliary protocol that will layout banner view in View Controller's view.
In this example we will layout at the bottom of screen.
```swift showLineNumbers title="BannerContainableController.swift"
protocol BannerContainableController: UIViewController {
var bannerContainerView: UIView! { get }
}
extension BannerContainableController {
func layoutBannerView(_ bannerView: APDBannerView) {
bannerView.removeFromSuperview()
bannerView.rootViewController = self
bannerContainerView.addSubview(bannerView)
NSLayoutConstraint.activate([
bannerView.topAnchor.constraint(equalTo: bannerContainerView.topAnchor),
bannerView.bottomAnchor.constraint(equalTo: bannerContainerView.bottomAnchor),
bannerView.leftAnchor.constraint(equalTo: bannerContainerView.leftAnchor),
bannerView.rightAnchor.constraint(equalTo: bannerContainerView.rightAnchor),
])
}
}
```
This protocol will be used in both `ParentViewController` and
`ChildViewController` to layout banner view.
2. Create new seque in storyboard with identifier: `ShowChildViewController`
3. Override `prepare(for segue:, sender:)` method in `ParentViewController`
and pass banner instance to `ChildViewController`.
```swift showLineNumbers title="ParentViewController.swift"
class ParentViewController: UIViewController, BannerContainableController {
@IBOutlet weak var bannerContainerView: UIView!
private lazy var bannerView = APDBannerView(size: kAPDAdSize320x50)
override func viewDidLoad() {
super.viewDidLoad()
// Load banner and it to the view in hierarchy
bannerView.loadAd()
bannerView.translatesAutoresizingMaskIntoConstraints = false
layoutBannerView(bannerView)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard
segue.identifier == "ShowChildViewController",
let childViewController = segue.destination as? ChildViewController
else { return }
childViewController.onDismiss = { [weak self] in
guard let self else { return }
self.layoutBannerView(self.bannerView)
}
childViewController.bannerView = bannerView
}
}
```
4. When user returns to `ParentViewController` we need to pass back
banner view. To achieve this we will use `onDismiss` callback.
```swift showLineNumbers title="ChildViewController.swift"
class ChildViewController: UIViewController, BannerContainableController {
@IBOutlet weak var bannerContainerView: UIView!
weak var bannerView: APDBannerView?
var onDismiss: (() -> ())?
override func viewDidLoad() {
super.viewDidLoad()
guard let bannerView = bannerView else { return }
layoutBannerView(bannerView)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
onDismiss?()
}
}
```
#### UINavigationController based application example.
1. Create auxiliary protocol that will layout banner view in View Controller's view.
We will use the same implementation from the previous storyboard example.
2. In `ParentViewController` create new instance of `ChildViewController`
and pass banner instance to it.
```swift showLineNumbers title="ParentViewController.swift"
class ParentViewController: UIViewController, BannerContainableController {
private lazy var bannerView = APDBannerView(size: kAPDAdSize320x50)
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Root View Controller"
bannerView.translatesAutoresizingMaskIntoConstraints = false
bannerView.rootViewController = self
bannerView.loadAd()
layoutBannerView(bannerView)
setupNavigationButton()
}
func setupNavigationButton() {
var configuration = UIButton.Configuration.filled()
configuration.titlePadding = 16
configuration.imagePadding = 16
let button = UIButton(configuration: configuration)
button.setImage(UIImage(systemName: "chevron.right"), for: .normal)
button.setTitle("Go To The Next View Controller", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(navigateToChildrenViewController), for: .touchUpInside)
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.heightAnchor.constraint(equalToConstant: 44)
])
}
@objc func navigateToChildrenViewController() {
let destinationViewController = ChildViewController()
destinationViewController.layoutBannerView(bannerView)
destinationViewController.onDismiss = { [weak self] in
guard let self = self else { return }
self.layoutBannerView(self.bannerView)
}
navigationController?.pushViewController(destinationViewController, animated: true)
}
}
```
3. In `ChildViewController` we will use the same approach to pass back
banner view on dismiss using closure.
```swift
class ChildViewController: UIViewController, BannerContainableController {
var onDismiss: (() -> ())?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
onDismiss?()
}
}
```
---
### BannerView in SwifUI based application
As for UIKit based application for SwiftUI we recommend to use
single banner instance for all you screens where banner is shown.
To achieve this behaviour you need to implement several auxiliary classes.
1. Create `APDBannerViewController` that will be using for banner view layout
and controlling of a view lifecycle
```swift showLineNumbers title="APDBannerViewController.swift"
final class APDBannerViewController: UIViewController {
weak var bannerView: APDBannerView?
var onDismiss: (() -> ())?
override func viewDidLoad() {
super.viewDidLoad()
layoutBannerView()
}
deinit {
onDismiss?()
}
func layoutBannerView() {
guard let bannerView = bannerView else { return }
bannerView.removeFromSuperview()
bannerView.rootViewController = self
view.addSubview(bannerView)
NSLayoutConstraint.activate([
bannerView.topAnchor.constraint(equalTo: view.topAnchor),
bannerView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
bannerView.leftAnchor.constraint(equalTo: view.leftAnchor),
bannerView.rightAnchor.constraint(equalTo: view.rightAnchor),
])
}
}
```
2. Create `BannerView` - a view that will implement `UIViewControllerRepresentable`
protocol and will be used in SwiftUI views hierarchy. Also we need to implement
custom `Coordinator` (singleton in this case) that uses `NSHashTable` to store
to manage `APDBannerViewController` instances.
```swift showLineNumbers title="BannerView.swift"
struct BannerView: UIViewControllerRepresentable {
typealias UIViewControllerType = APDBannerViewController
func makeUIViewController(context: Context) -> APDBannerViewController {
let controller = APDBannerViewController()
controller.bannerView = context.coordinator.bannerView
controller.onDismiss = context.coordinator.dismiss
context.coordinator.store(controller)
return controller
}
func updateUIViewController(
_ uiViewController: APDBannerViewController,
context: Context
) {}
func makeCoordinator() -> Coordinator {
return .shared
}
final class Coordinator {
static let shared = Coordinator()
private lazy var storage = NSHashTable(options: .weakMemory)
lazy var bannerView: APDBannerView = {
let bannerView = APDBannerView(size: kAPDAdSize320x50)
bannerView.translatesAutoresizingMaskIntoConstraints = false
bannerView.loadAd()
return bannerView
}()
func store(_ controller: APDBannerViewController) {
storage.add(controller)
}
func dismiss() {
let previousController = storage.allObjects.last
previousController?.bannerView = bannerView
previousController?.layoutBannerView()
}
}
}
```
3. Now we can use `BannerView` in SwiftUI views hierarchy.
:::caution
BannerView is requred to have frame with fixex height.
:::
```swift showLineNumbers title="ContentView.swift"
struct ContentView: View {
var body: some View {
NavigationStack {
VStack {
BannerView()
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color(uiColor: .secondarySystemFill))
Spacer()
NavigationLink(
destination: {
ChildView()
},
label: {
HStack {
Image(systemName: "chevron.right")
Text("Go To The Next View")
}
.foregroundColor(.white)
.padding()
.background(RoundedRectangle(cornerRadius: 8).fill(Color.blue))
}
)
}
.navigationTitle("Root View")
}
}
}
struct ChildView: View {
var body: some View {
NavigationStack {
VStack {
Spacer()
BannerView()
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color(uiColor: .secondarySystemFill))
}
.navigationTitle("Child View")
}
}
}
```
---
### Enable Smart Banners
Smart banners are banner ads which automatically fit the
screen/container size. Using them helps to deal with increasing
fragmentation of the screen sizes on different devices. To enable them,
use the following method:
```swift showLineNumbers
//for top/bottom banners allows banner view to resize automatically to fit device screen
Appodeal.setSmartBannersEnabled(true)
//for banner view allows banner view to resize automatically to fit device screen
bannerView.usesSmartSizing = true
```
```objc showLineNumbers
//for top/bottom banners allows banner view to resize automatically to fit device screen
[Appodeal setSmartBannersEnabled:YES];
//for banner view allows banner view to resize automatically to fit device screen
bannerView.usesSmartSizing = YES;
```
---
### Change Banner Background
This method allows to create a grey background for banner ads:
```swift showLineNumbers
//for top/bottom banners
Appodeal.setBannerBackgroundVisible(true)
//for bannerView
bannerView.backgroundVisible = true
```
```objc showLineNumbers
//for top/bottom banners
[Appodeal setBannerBackgroundVisible: YES];
//for bannerView
[bannerView setBannerBackgroundVisible: YES];
```
---
### Enable Banner Refresh Animation
```swift showLineNumbers
//for top/bottom banners
Appodeal.setBannerAnimationEnabled(true)
//for bannerView
bannerView.bannerAnimationEnabled = true
```
```objc showLineNumbers
//for top/bottom banners
[Appodeal setBannerAnimationEnabled:YES];
//for bannerView
[bannerView setBannerAnimationEnabled:YES];
```
---
### Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```swift showLineNumbers
Appodeal.predictedEcpm(for: .banner)
```
```objc showLineNumbers
[Appodeal predictedEcpmForAdType: AppodealAdTypeBanner];
```
### Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the following log if show was tracked successfully.
``` c#
[Appodeal *.*.*] [debug] [impression] Impression succesfully tracked
```
---
---
## Interstitial(Ad-types)
Interstitial ads are full-screen ads. In Appodeal, they are divided into
two types - static interstitial and video interstitial.
Both ad types are requested when caching, the one shown being the more
expensive of the two.
Static interstitial - static full-screen
banners.
Video interstitial - videos that can be closed 5 seconds after
the start.
You can use our **demo app** as a reference project.
## Check If Ad Is Loaded
You can check if the ad has been loaded before showing it. This method
returns a boolean value indicating whether or not the interstitial has
been loaded.
```swift showLineNumbers
Appodeal.isReadyForShow(with: .interstitial)
```
```objc showLineNumbers
[Appodeal isReadyForShowWithStyle: AppodealShowStyleInterstitial];
```
We recommend you to check ad caching before trying to show it.
## Display
```swift showLineNumbers
Appodeal.showAd(AppodealShowStyle.interstitial, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleInterstitial rootViewController:self];
```
## Manual Caching
By default, auto caching is enabled: Appodeal SDK starts to load
Interstitial right after the initialization method is called.
The next interstitial ad starts to load after the previous one has been
closed.
To disable automatic caching for interstitials, use the code below
before SDK initialization:
```swift showLineNumbers
Appodeal.setAutocache(false, types: .interstitial)
```
```objc showLineNumbers
[Appodeal setAutocache: NO types:AppodealAdTypeInterstitial];
```
To cache an interstitial, use:
```swift showLineNumbers
Appodeal.cacheAd(.interstitial)
```
```objc showLineNumbers
[Appodeal cacheAd:AppodealAdTypeInterstitial];
```
Read more on manual caching in our [FAQ](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
## Callbacks
Callbacks are used to track different events in the lifecycle of an ad,
e.g., when an ad was clicked on or closed. To get them, you need to set
the delegate as follows:
```swift showLineNumbers
// set delegate
Appodeal.setInterstitialDelegate(self)
```
```objc showLineNumbers
// set delegate
[Appodeal setInterstitialDelegate:self];
```
Usually, the class that implements interstitials is also the delegate
class. That's why the delegate property can be set to `self`.
Now you can use the following callback methods:
```swift showLineNumbers
extension YourViewController: AppodealInterstitialDelegate {
// Method called when precache (cheap and fast load) or usual interstitial view did load
//
// - Warning: If you want show only expensive ad, ignore this callback call with precache equal to YES
// - Parameter precache: If precache is YES it's mean that precache loaded
func interstitialDidLoadAdIsPrecache(_ precache: Bool) {
}
// Method called if interstitial mediation failed
func interstitialDidFailToLoadAd() {
}
// Method called if interstitial mediation was success, but ready ad network can't show ad or
// ad presentation was to frequently according your placement settings
func interstitialDidFailToPresent() {
}
// Method called when interstitial will display on screen
func interstitialWillPresent() {
}
// Method called after interstitial leave screeen
func interstitialDidDismiss() {
}
// Method called when user tap on interstitial
func interstitialDidClick() {
}
// Method called when interstitial did expire and could not be shown
func interstitialDidExpired(){
}
}
```
```objc showLineNumbers
- (void)interstitialDidLoadAdIsPrecache:(BOOL)precache; //interstitial was loaded (precache flag shows if the loaded ad is precache)
- (void)interstitialDidFailToLoadAd; //interstitial failed to load
- (void)interstitialDidFailToPresent; //interstitial was loaded but failed to present (may be caused by inner ad network error, placement settings or invalid creative)
- (void)interstitialWillPresent; //interstitial was loaded and will present
- (void)interstitialDidDismiss; //interstitial was closed
```
:::tip
All callbacks are called on the main thread.
:::
## Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```swift showLineNumbers
Appodeal.showAd(.interstitial, forPlacement: placement, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleInterstitial forPlacement:placement rootViewController:self];
```
If the loaded ad can't be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement), check if the ad can be shown
before calling show method:
```swift showLineNumbers
Appodeal.canShow(.interstitial, forPlacement: placement)
```
```objc showLineNumbers
[Appodeal canShow:AppodealAdTypeInterstitial forPlacement:placement];
```
You can configure your impression logic for each placement.
If you have no placements or call Appodeal.show with a placement that
does not exist, the impression will be tagged with 'default' placement
with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
## Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```swift showLineNumbers
Appodeal.predictedEcpm(for: .interstitial)
```
```objc showLineNumbers
[Appodeal predictedEcpmForAdType: AppodealAdTypeInterstitial];
```
## Check If Ad Is Initialized
```swift showLineNumbers
Appodeal.predictedEcpm(for: .interstitial)
```
```objc showLineNumbers
[Appodeal predictedEcpmForAdType: AppodealAdTypeInterstitial];
```
Returns `true` if interstitial was initialized.
## Check If Autocache Is Enabled
```swift showLineNumbers
Appodeal.isAutocacheEnabled(.interstitial)
```
```objc showLineNumbers
[Appodeal isAutocacheEnabled: AppodealAdTypeInterstitial];
```
Returns `true` if autocache is enabled for interstitial.
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the following log if show was tracked successfully.
``` c#
[Appodeal *.*.*] [debug] [impression] Impression succesfully tracked
```
---
## MREC(Ad-types)
:::important View-Based Ad Type
MREC is a **view-based** ad format that behaves differently from other Appodeal ad types. Unlike interstitial or rewarded video ads, MREC ads are displayed as views that need to be manually added to your view hierarchy.
:::
MREC is a 300x250 banner. This type can be useful if the application
interface has a large free area for placing a banner.
You can use our **demo app** as a reference project.
## Check If Ad Is Loaded
:::warning Important
For MREC ads, you must use the `mrec.isReady` property to check if an ad is loaded and ready to display.
**Do NOT use** `Appodeal.isLoaded(.MREC)` or `Appodeal.canShow(.MREC)` as they are unreliable for MREC and will return `false` even when an ad is loaded and ready to show.
:::
```swift showLineNumbers
// MREC view - CORRECT way to check if loaded
mrec.isReady
// ❌ INCORRECT - These methods are unreliable for MREC:
// Appodeal.isLoaded(.MREC) // Will return false even when loaded
// Appodeal.canShow(.MREC) // Will return false even when loaded
```
```objc showLineNumbers
// MREC view - CORRECT way to check if loaded
mrec.isReady;
// ❌ INCORRECT - These methods are unreliable for MREC:
// [Appodeal isLoaded:AppodealAdTypeMREC]; // Will return false even when loaded
// [Appodeal canShow:AppodealAdTypeMREC]; // Will return false even when loaded
```
------------------
## Display
`AppodealMRECView` is a subclass of `AppodealBannerView`. The size of
`AppodealMRECView` is 300x250.
MREC ads are refreshed every 15 seconds automatically by default. To
display MREC, you need to call the following code:
```swift showLineNumbers
class YourViewController: UIViewController, AppodealBannerViewDelegate {
override func viewDidLoad () {
super.viewDidLoad()
// required: init ad banner
let mrecView: AppodealMRECView = AppodealMRECView()
mrecView.usesSmartSizing = false
mrecView.rootViewController = self
// optional: set delegate
mrecView.delegate = self
// required: add banner to superview and call -loadAd to start banner loading
self.view.addSubview(mrecView)
mrecView.loadAd()
}
// optional: implement any of AppodealBannerViewDelegate methods
func bannerViewDidLoadAd(_ bannerView: APDBannerView, isPrecache precache: Bool) {
NSLog("bannerView was loaded")
}
func bannerView(_ bannerView: APDBannerView, didFailToLoadAdWithError error: Error) {
NSLog("bannerView failed to load");
}
func bannerViewDidInteract(_ bannerView: APDBannerView) {
NSLog("bannerView was clicked")
}
func bannerViewDidShow(_ bannerView: APDBannerView) {
NSLog("bannerView was shown")
}
func bannerViewExpired(_ bannerView: APDBannerView) {
NSLog("bannerView did expire and could not be shown")
}
}
```
```objc showLineNumbers
#import "YourViewController.h"
#import
interface YourViewController ()
@end
@implementation YourViewController
- (void)viewDidLoad {
[super viewDidLoad];
// required: init ad banner
AppodealMRECView *mrecView= [[AppodealMRECView alloc] initWithRootViewController:self];
// optional: set delegate
mrecView.delegate = self;
// required: add banner to superview and call loadAd to start banner loading
[self.view addSubview:mrecView];
[mrecView loadAd];
}
// optional: implement any of AppodealBannerViewDelegate methods
- (void)bannerViewDidLoadAd:(APDBannerView *)bannerView {
NSLog(@"Banner %@ did load!", bannerView);
}
- (void)bannerViewDidInteract:(APDBannerView *)bannerView {
NSLog(@"Banner %@ did interact", bannerView);
}
- (void)bannerView:(APDBannerView *)bannerView didFailToLoadAdWithError:(NSError *)error {
NSLog(@"Banner %@ did fail with error: %@", bannerView, error);
}
- (void)bannerViewDidRefresh:(APDBannerView *)bannerView {
NSLog(@"Banner %@ did refresh ", bannerView);
}
@end
```
------------------
## Manual Caching
MREC does not support autocache.
To cache MREC use:
```swift showLineNumbers
// MREC view
mrec.loadAd()
```
```objc showLineNumbers
// MREC view
[mrec loadAd];
```
------------------
Read more on manual caching in our [FAQ](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
## Callbacks
Callbacks are used to track different events of an ad's lifecycle, e.g.,
when a MREC has been successfully loaded or is about to appear. To get
them, you need to set the delegate as follows:
```swift showLineNumbers
//set delegate
Appodeal.setBannerDelegate(self)
```
```objc showLineNumbers
//set delegate
[Appodeal setBannerDelegate:self];
```
------------------
Usually, the class that implements banners is also the delegate class.
That's why the delegate property can be set to `self`.
Now you can use the following callbacs methods:
```swift showLineNumbers
// banner was loaded (precache flag shows if the loaded ad is precache)
func bannerDidLoadAdIsPrecache(_ precache: Bool) {}
// banner was shown
func bannerDidShow() {}
// banner failed to load
func bannerDidFailToLoadAd() {}
// banner was clicked
func bannerDidClick() {}
// banner did expire and could not be shown
func bannerDidExpired() {}
```
```objc showLineNumbers
- (void)bannerDidLoadAdIsPrecache:(BOOL)precache {
// banner was loaded (precache flag shows if the loaded ad is precache)
}
- (void)bannerDidShow {
// banner was shown
}
- (void)bannerDidFailToLoadAd {
// banner failed to load
}
- (void)bannerDidClick {
// banner was clicked
}
- (void)bannerDidExpired {
// banner did expire and could not be shown
}
```
------------------
:::note
All callbacks are called on the main thread.
:::
## Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```swift showLineNumbers
Appodeal.showAd(.MREC, forPlacement: placement, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleMREC forPlacement:placement rootViewController:self];
```
------------------
:::warning Important for MREC
**Do not use** `Appodeal.canShow(.MREC, forPlacement:)` to check if an ad can be shown for a placement. This method is unreliable for MREC and will return `false` even when an ad is ready to display.
Instead, use `mrec.isReady` to check if the MREC view has a loaded ad before displaying it.
:::
If the loaded ad can't be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate.
------------------
You can configure your impression logic for each placement.
If you have no placements or call Appodeal.show with a placement that
does not exist, the impression will be tagged with 'default' placement
with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
## Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```swift showLineNumbers
Appodeal.predictedEcpm(for: .MREC)
```
```objc showLineNumbers
[Appodeal predictedEcpmForAdType: AppodealAdTypeMREC];
```
------------------
## Check If Ad is Initialized
```swift showLineNumbers
Appodeal.isInitialized(for: .MREC)
```
```objc showLineNumbers
[Appodeal isInitalizedForAdType: AppodealAdTypeMREC];
```
------------------
Returns `true` if MREC was initialized.
## Check if Autocache Is Enabled For Ad
MREC does not support autocache.
## Hide
MREC is a view. To hide MREC, remove it from superView
```swift showLineNumbers
// MREC view
mrec.removeFromSuperview()
```
```objc showLineNumbers
// MREC view
[mrec removeFromSuperview];
```
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the following log if show was tracked successfully.
``` c#
[Appodeal *.*.*] [debug] [impression] Impression succesfully tracked
```
------------------
---
## Native(Ad-types)
Native ad is a flexible type of advertising. You can adapt the display for your UI by preparing a template.
You can use our **demo app** as a reference project.
## Integration
Native AdQueue is the native ad implementation and management tool in Appodeal SDK.
You no longer need to load Native Ads manually. All you have to do is to set the AdQueue object, and it will load new items automatically.
Be careful when using AdQueue: if your app loads too many ads, but is not able to use them,
the ad network can either lower the cost of each impression for you or limit your ability to load native ads.
```swift showLineNumbers
class ViewController: UIViewController {
var adQueue : APDNativeAdQueue!
}
```
```objc showLineNumbers
#import
@interface YourViewController : UIViewController
@property (nonatomic, strong) APDNativeAdQueue* nativeAdQueue;
@end
```
---------------------
:::info Native ads requirements
- All of the native ad fields marked as mandatory must be displayed.
- Every ad should have a sign that clearly indicates that it is an ad. For example "Ad" or "Sponsored".
- Provided images can be resized to fit your ad space but should not be significantly distorted or cropped.
:::
## Configure Native Ad Settings
In `adQueue.settings` you can set the following parameters for the native ads displayed in your app:
| adQueue.settings setting name | Type | Appointment | Possible values |
| -------------- | -------------- | -------------- | -------------- |
| type | APDNativeAdType | Native Ad Type | `APDNativeAdTypeAuto`, `APDNativeAdTypeNoVideo`, `APDNativeAdTypeVideo` |
| adViewClass | Class APDNativeAdView | Ad view template class | Default: `APDDefaultNativeAdView`. Set your own class to customize the layout. |
| ~~autocacheMask~~ | APDNativeResourceAutocacheMask | Deprecated from v3.2.0 | `APDNativeResourceAutocacheIcon`, `APDNativeResourceAutocacheMedia` |
## Initialize A Specific Native Ad Type
Appodeal SDK provides both static and video types of native ads.
To implement static native ads in your app, use the following code:
```swift showLineNumbers
class ViewController: UIViewController {
var adQueue : APDNativeAdQueue!
override func viewDidLoad() {
super.viewDidLoad()
adQueue.settings.adViewClass = APDDefaultNativeAdView.self
adQueue.settings.type = .novideo
adQueue.loadAd()
}
}
```
```objc showLineNumbers
- (void)viewDidLoad {
self.nativeAdQueue = [APDNativeAdQueue new];
self.nativeAdQueue.settings.type = APDNativeAdTypeNoVideo;
self.nativeAdQueue.settings.adViewClass = APDDefaultNativeAdView.class;
[self.nativeAdQueue loadAd];
}
```
--------------------
To implement native video ads, use the following code:
```swift showLineNumbers
class ViewController: UIViewController {
var adQueue : APDNativeAdQueue!
override func viewDidLoad() {
super.viewDidLoad()
adQueue.settings.adViewClass = APDDefaultNativeAdView.self
adQueue.settings.type = .video
adQueue.loadAd()
}
}
```
```objc showLineNumbers
#import
@interface YourViewController : UIViewController
@property (nonatomic, strong) APDNativeAdQueue* nativeAdQueue;
@property (nonatomic, strong) UIView * nativeAdView;
@end
@implementation YourViewController
- (void)viewDidLoad {
self.nativeAdQueue = [[APDNativeAdQueue alloc] init];
self.nativeAdQueue.settings.type = APDNativeAdTypeVideo;
self.nativeAdQueue.settings.adViewClass = APDDefaultNativeAdView.class;
self.nativeAdQueue.delegate = self;
[self.nativeAdQueue loadAd];
}
@end
```
--------------------
The aspect ratio of native video ads in Appodeal SDK is 16:9, the file size is about 1-3 Mb.
There are two types of native video ads in Appodeal SDK:
- **skippable** - if “skippable” flag is stated, the video can be skipped after 5 seconds;
- **muted** - if “muted” flag is stated, the video will be played with no sound.
## Caching
Native ads will start downloading when a native ad queue instance is created.
You don't need to control the loading lifecycle. But if you bring some ad from ad queue, you need to have strong reference to it during
the whole native ad presentation time. Native ad doesn't have a strong reference to view and view doesn't have a strong reference on the native ad.
If application loses reference to the native ad after the impression, the native ad won't be tracking any events.
## Callbacks
Callbacks are used to track different events in the lifecycle of an ad, e.g.,
when an ad was clicked on or closed. To get them, you need to set the delegate as follows:
1. Add `APDNativeAdQueueDelegate` and `APDNativeAdPresentationDelegate` to the header file:
```swift showLineNumbers
class YourViewController: APDNativeAdQueueDelegate, APDNativeAdPresentationDelegate { }
```
```objc showLineNumbers
@interface YourViewController : UIViewController
```
---------------------
2. Set the delegate:
```swift showLineNumbers
// Loading callbacks delegate
self.adQueue.delegate = self
// Presentation callbacks delegate
self.currentAd.delegate = self
```
```objc showLineNumbers
// Loading callbacks delegate
self.adQueue.delegate = self;
// Presentation callbacks delegate
self.currentAd.delegate = self;
```
---------------------
3. Implement the following functions:
```swift showLineNumbers
extension MainViewController : APDNativeAdPresentationDelegate {
func nativeAdWillLogImpression(_ nativeAd: APDNativeAd!) {}
func nativeAdWillLogUserInteraction(_ nativeAd: APDNativeAd!) {}
}
extension MainViewController : APDNativeAdQueueDelegate {
func adQueue(_ adQueue: APDNativeAdQueue!, failedWithError error: Error!) {}
func adQueueAdIsAvailable(_ adQueue: APDNativeAdQueue!, ofCount count: UInt) {}
}
```
```objc showLineNumbers
- (void)adQueueAdIsAvailable:(APDNativeAdQueue *)adQueue ofCount:(NSUInteger)count {
}
- (void)adQueue:(APDNativeAdQueue *)adQueue failedWithError:(NSError *)error {
}
- (void)nativeAdWillLogImpression:(APDNativeAd *)nativeAd {
}
- (void)nativeAdWillLogUserInteraction:(APDNativeAd *)nativeAd {
}
```
---------------------
:::tip
All callbacks are called on the main thread.
:::
## Use Custom Native Ad Templates
To use your custom templates for native ads, simply state your template class in `adQueue.setting.adViewClass` as follows:
```swift showLineNumbers
adQueue.settings.adViewClass = YourNativeAdViewTemplate.self
```
```objc showLineNumbers
adQueue.settings.adViewClass = YourNativeAdViewTemplate.class;
```
-------------------
Your template class should conform to the following protocol:
```swift showLineNumbers
protocol APDNativeAdView {
func titleLabel() -> UILabel
func callToActionLabel() -> UILabel
// Optional
func descriptionLabel() -> UILabel
func iconView() -> UIImageView
func mediaContainerView() -> UIView
func contentRatingLabel() -> UILabel
func adChoicesView() -> UIView
func setRating(_ rating: NSNumber)
static func nib() -> UINib
}
```
```objc showLineNumbers
@protocol APDNativeAdView
- (nonnull UILabel *)titleLabel;
- (nonnull UILabel *)callToActionLabel;
@optional
- (nonnull UILabel *)descriptionLabel;
- (nonnull UIImageView *)iconView;
- (nonnull UIView *)mediaContainerView;
- (nonnull UILabel *)contentRatingLabel;
- (nonnull UIView *)adChoicesView;
- (void)setRating:(nonnull NSNumber *)rating;
+ (nonnull UINib *)nib;
@end
```
-------------------
The objects mentioned in this protocol are:
- `titleLabel` - container for title text;
- `callToActionLabel` - container for call-to-action text;
- `descriptionLabel` - container for description text;
- `iconView` - container for icon image;
- `mediaContainerView` - container for media files (images and video files);
- `contentRatingLabel` - container for showing rating of the content;
- `adChoicesView` - container for showing adChoice;
- `rating` - container for showing rating of the app;
- `nib` - nib-file for template.
:::note
All views should be enclosed in a single `superview`.
If `YourNativeAdViewTemplate` inherits from `UITableViewCell(UICollectionViewCell)`, these views should be contained in the hierarchy of `contentView`.
:::
## Get All Native Ads From Native AdQueue
```swift showLineNumbers
class ViewController: UIViewController {
@IBOutlet weak var nativeAdView: UIView!
var nativeAdQueue: APDNativeAdQueue!
var nativeArray: [APDNativeAd] = []
override func viewDidLoad() {
super.viewDidLoad()
nativeAdQueue = APDNativeAdQueue()
nativeAdQueue.settings = APDNativeAdSettings.default()
nativeAdQueue.settings.adViewClass = CustomNativeAdView.self
nativeAdQueue.delegate = self
nativeAdQueue.loadAd()
}
@IBAction func presentNativeAd(_ sender: Any) {
let nativeAd = nativeArray.first
if let nativeAd = nativeAd {
nativeAd.delegate = self
do {
let adView = try nativeAd.getViewForPlacement("default", withRootViewController: self)
adView.frame = nativeAdView.bounds
nativeAdView.addSubview(adView)
} catch {
print("error")
}
}
}
}
extension ViewController: APDNativeAdQueueDelegate, APDNativeAdPresentationDelegate {
func adQueueAdIsAvailable(_ adQueue: APDNativeAdQueue, ofCount count: UInt) {
if nativeArray.count > 0 {
return
} else {
nativeArray.append(contentsOf: adQueue.getNativeAds(ofCount: 1))
nativeArray.map{( $0.delegate = self )}
}
}
}
```
```objc showLineNumbers
#import "ViewController.h"
#import
#import
#import "CustomNativeAdView.h"
@interface ViewController ()
@property (strong, nonatomic) IBOutlet UIView *nativeAdView;
@property (nonatomic, strong) APDNativeAdQueue* nativeAdQueue;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.nativeAdQueue = [[APDNativeAdQueue alloc] init];
self.nativeAdQueue.settings = [APDNativeAdSettings defaultSettings];
self.nativeAdQueue.settings.adViewClass = [CustomNativeAdView class];
self.nativeAdQueue.delegate = self;
[self.nativeAdQueue loadAd];
[Appodeal setLogLevel:APDLogLevelVerbose];
}
- (void)presentNative:(id)sender {
APDNativeAd * nativeAd = [[self.nativeAdQueue getNativeAdsOfCount:1] firstObject];
nativeAd.delegate = self;
CustomNativeAdView * adview = [nativeAd getAdViewForController:self];
[self.nativeAdView addSubview:adview];
adview.frame = self.nativeAdView.bounds;
}
@end
```
-------------------
## Native Ad Object
NativeAd objects have the following characteristics in Appodeal SDK. All of the fields marked as “Mandatory” must be displayed:
| Name of field | Required? | Description
| -------------- | -------------- | -------------- |
| **NSString *title** | Mandatory | Native ad title. At least 25 characters of the title should always be displayed. You can add ellipsis at the end if the title is longer. |
| **NSString *descriptionText** | Optional | Text description of the native ad. If you choose to display the description, you should display at least 75 characters. You can add ellipsis at the end. |
| **NSString *callToActionText** | Mandatory | The call to action text. Should be displayed on a visible button without truncation. |
| **NSString *contentRating** | Optional | Rating of the content. |
| **NSNumber *starRating** | Optional | Rating of the app in the [0-5] range. |
| **APDImage *mainImage** | Optional | Bitmap of an image. An ad object contains both icon and image. It's mandatory to use at least one of these elements. |
| **APDImage *iconImage** | Mandatory | Square icon image. Prevalent sizes are 50x50 and 80x80. |
| **UIView *adChoicesView** | Mandatory | View. If it doesn't return `null`, it's mandatory to display the provider icon in any corner of the native ad. Used by some networks to display AdChoices or the privacy icon. |
## Common Mistakes With Native Ads
- **No ad attribution or AdChoices icon**
The majority of ad networks require publishers to add a special mark to native ads, so users don't mistake them for content.
That's why you always need to make sure, that native ads in your app have the ad attribution (e.g., “Ad”) or the AdChoices icon.
- **Absence of the required native ad elements**
Every native must contain:
- title;
- call-to-action button;
- ad attribution or AdChoices icon;
- icon, image or video.
- **Native ad elements alteration**
Advertisers expect that their ads will be displayed clearly and without any alteration.
You can scale buttons and images, but you shouldn't crop, cover or distort them.
- **Overlapping native ad elements**
Make sure that all native ad elements are visible and do not overlap.
## Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is calculated based on historical data for the current ad unit.
```swift showLineNumbers
Appodeal.predictedEcpm(for: .nativeAd)
```
```objc showLineNumbers
[Appodeal isInitalizedForAdType: AppodealAdTypeNativeAd];
```
---------------------
## Check If Native Ad Is Initialized
```swift showLineNumbers
Appodeal.isInitialized(for: .nativeAd)
```
```objc showLineNumbers
[Appodeal isInitalizedForAdType: AppodealAdTypeNativeAd];
```
---------------------
Returns `true`, if the Native has been initialized.
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the following log if show was tracked successfully.
``` c#
[Appodeal *.*.*] [debug] [impression] Impression succesfully tracked
```
---
## Rewarded Video(Ad-types)
Rewarded videos are user-initiated ads where users can earn in-app
rewards in exchange for viewing a video.
You can use our **demo app** as a reference project.
## Check If Ad Is Loaded
You can check if the ad has been loaded before showing it. This method
returns a boolean value indicating whether or not the Rewarded Video has
been loaded.
```swift showLineNumbers
Appodeal.isReadyForShow(with: .rewardedVideo)
```
```objc showLineNumbers
[Appodeal isReadyForShowWithStyle: AppodealShowStyleRewardedVideo];
```
------------------
We recommend you to check ad caching before trying to show it.
## Display
```swift showLineNumbers
Appodeal.showAd(.rewardedVideo, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleRewardedVideo rootViewController:self];
```
------------------
## Manual Caching
By default, auto caching is enabled: Appodeal SDK starts to load
Rewarded Video right after the initialization method is called.
The next Rewarded Video ad starts to load after the previous one has
been closed.
To disable automatic caching for Rewarded Video, use the code below
before SDK initialization:
```swift showLineNumbers
Appodeal.setAutocache(false, types: .rewardedVideo)
```
```objc showLineNumbers
[Appodeal setAutocache: NO types:AppodealAdTypeRewardedVideo];
```
------------------
If you need more control over Rewarded video ad loading, use manual caching.
Manual caching for Rewarded videos can be useful to **improve display rate** or decrease SDK load when several ad types are cached.
To cache a Rewarded Video, use:
```swift showLineNumbers
Appodeal.cacheAd(.rewardedVideo)
```
```objc showLineNumbers
[Appodeal cacheAd:AppodealAdTypeRewardedVideo];
```
------------------
Read more on manual caching in our [FAQ](/faq-and-troubleshooting/troubleshooting/general/sdk-caching).
## Callbacks
Callbacks are used to track different events in the lifecycle of an ad,
e.g., when an ad was clicked on or closed. To get them, you need to set
the delegate as follows:
```swift showLineNumbers
// set delegate
Appodeal.setRewardedVideoDelegate(self)
```
```objc showLineNumbers
// set delegate
[Appodeal setRewardedVideoDelegate:self];
```
------------------
Usually, the class that implements rewarded video ads is also the
delegate class. That's why the delegate property can be set to `self`.
Now you can use the following callback methods:
```swift showLineNumbers
extension YourViewController: AppodealRewardedVideoDelegate {
// Method called when rewarded video loads
//
// - Parameter precache: If precache is YES it means that precached ad loaded
func rewardedVideoDidLoadAdIsPrecache(_ precache: Bool) {
}
// Method called if rewarded video mediation failed
func rewardedVideoDidFailToLoadAd() {
}
// Method called if rewarded mediation was successful, but ready ad network can't show ad or
// ad presentation was too frequent according to your placement settings
//
// - Parameter error: Error object that indicates error reason
func rewardedVideoDidFailToPresentWithError(_ error: Error) {
}
// Method called after rewarded video start displaying
func rewardedVideoDidPresent() {
}
// Method called before rewarded video leaves screen
//
// - Parameter wasFullyWatched: boolean flag indicated that user watch video fully
func rewardedVideoWillDismissAndWasFullyWatched(_ wasFullyWatched: Bool) {
}
// Method called after fully watch of video
//
// - Warning: After call this method rewarded video can stay on screen and show postbanner
// - Parameters:
// - rewardAmount: Amount of app curency tuned via Appodeal Dashboard
// - rewardName: Name of app currency tuned via Appodeal Dashboard
func rewardedVideoDidFinish(_ rewardAmount: Float, name rewardName: String?) {
}
// Method is called when rewarded video is clicked
func rewardedVideoDidClick() {
}
// Method called when rewardedVideo did expire and can not be shown
func rewardedVideoDidExpired() {
}
}
```
```objc showLineNumbers
- (void)rewardedVideoDidLoadAdIsPrecache:(BOOL)precache {
// rewarded video was loaded
}
- (void)rewardedVideoDidFailToLoadAd {
// rewarded video ad failed to load
}
- (void)rewardedVideoDidFailToPresentWithError:(NSError *)error {
// rewarded video ad was loaded but failed to present due to ad netwotk error,
// placement settings or invalid creative.
// Error object that indicates error reason
}
- (void)rewardedVideoDidPresent {
// rewarded video was presented
}
- (void)rewardedVideoWillDismissAndWasFullyWatched:(BOOL)wasFullyWatched {
// rewarded video was closed.
// wasFullyWatched boolean flag indicated that user watch video fully
}
- (void)rewardedVideoDidFinish:(float)rewardAmount name:(NSString *)rewardName {
// rewarded video finished with some reward
}
- (void)rewardedVideoDidClick {
// Method is called when rewarded video is clicked
}
- (void)rewardedVideoDidExpired {
// rewarded video did expire and could not be shown
}
```
------------------
:::tip
All callbacks are called on the main thread.
:::
## Placements
Appodeal SDK allows you to tag each impression with different placement.
To be able to use placements, you need to create them in Appodeal
Dashboard. [Read more](/advanced/placements) about placements.
```swift showLineNumbers
Appodeal.showAd(.rewardedVideo, forPlacement: placement, rootViewController: self)
```
```objc showLineNumbers
[Appodeal showAd:AppodealShowStyleRewardedVideo forPlacement:placement rootViewController:self];
```
------------------
If the loaded ad can’t be shown in a specific placement, nothing will be
shown. If auto caching is enabled, the SDK will start to cache another
ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement) check if the ad can be shown
before calling show method:
```swift showLineNumbers
Appodeal.canShow(.rewardedVideo, forPlacement: placement)
```
```objc showLineNumbers
[Appodeal canShow:AppodealAdTypeRewardedVideo forPlacement:placement];
```
------------------
You can configure your impression logic for each placement.
If you have no placements or call `Appodeal.show` with a placement
that does not exist, the impression will be tagged with 'default'
placement with corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
## Server-to-Server Callbacks
To secure your apps economy we offer S2S reward callbacks. To validate each reward, you need to set up a callback URL
on your server that will receive the reward information. We will pass the user data to your callback URL,
which you will need to validate and adjust the user balance accordingly.
1. Create the reward callback URL on your server that will receive the reward information.
2. Fill the created URL and the encryption key in the app settings in your dashboard.
3. The reward callback will be sent to your URL using GET request with two parameters:
```as3
{http:/example.com/reward}?data1={data1}&data2={data2}
```
4. Your URL should decrypt the data and validate it.
5. Check `impression_id` for uniqueness and store it in your system to prevent duplicate transactions.
To set user ID, use the `Appodeal.getUserSettings(this).setUserId("User#123")` method before SDK initialization.
We offer sample scripts in Go, PHP, Ruby, Java, Node.js, Python 3 and C# to decrypt the data.
If you need samples in other languages, please contact our support team and we will provide them to you.
- Sample in PHP: [reward.php](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.php).
- Sample in Ruby: [reward.rb](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.rb).
- Sample in Java: [reward.java](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/Reward.java).
- Sample in Node.js: [reward.js](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.js) .
- Sample in Python 3: [reward.py](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.py).
- Sample in C#: [reward.cs](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/Reward.cs).
- Sample in Go: [reward.go](https://appodeal-android.s3-us-west-1.amazonaws.com/reward/reward.go).
## Getting Reward Data For A Specific Placement
To get reward details (currency, name and amount) for any placement, use
the `rewardForPlacement:(NSString *)placement` method:
```swift showLineNumbers
let rewardCurrencyName = Appodeal.reward(forPlacement:"placement").currencyName
let rewardAmount = Appodeal.reward(forPlacement:"placement").amount
```
```objc showLineNumbers
NSString *rewardCurrencyName = [[Appodeal rewardForPlacement:@"placement"] currencyName];
NSUInteger rewardAmount = [[Appodeal rewardForPlacement:@"placement"] amount];
```
------------------
## Get Predicted eCPM
This method returns the expected eCPM for the cached ad. The amount is
calculated based on historical data for the current ad unit.
```swift showLineNumbers
Appodeal.predictedEcpm(for: .rewardedVideo)
```
```objc showLineNumbers
[Appodeal predictedEcpmForAdType: AppodealAdTypeRewardedVideo];
```
------------------
## Check If Ad Is Initialized
```swift showLineNumbers
Appodeal.isInitialized(for: .rewardedVideo)
```
```objc showLineNumbers
[Appodeal isInitalizedForAdType: AppodealAdTypeRewardedVideo];
```
------------------
Returns `true` if the Rewarded Video has been initialized.
## Check If Autocache Is Enabled
```swift showLineNumbers
Appodeal.isAutocacheEnabled(.rewardedVideo)
```
```objc showLineNumbers
[Appodeal isAutocacheEnabled: AppodealAdTypeRewardedVideo];
```
------------------
Returns `true` if autocache is enabled for Rewarded Video.
## Check Viewability
You can always check in logs if show was tracked and your ad is visible.
You will see the following log if show was tracked successfully.
``` c#
[Appodeal *.*.*] [debug] [impression] Impression succesfully tracked
```
---
## Adjust(Services)
The Appodeal SDK gives you tools to grow your mobile apps & games.
Adjust is one of them.
Use the Adjust account to track your attribution & analytics
metrics from your UA campaigns.
Evaluate your soft launch and other marketing campaigns from the
Appodeal Reports page that you will find inside your Appodeal Dashboard.
- Compare Ads vs. IAPs vs. subscription revenues
- Get Forecasted LTV based on UA campaigns
- Find out which Ad Creatives bring top-paying users
- Sync your retention metrics with your ARPU & revenues
- Build deep granular reports to find out new growth opportunities
We have two options for linking Adjust:
- **Our Adjust account.**
:::info
There is a limit of 10 000 non-organic installs per month.
If you are planning to run UA campaigns in near future, you can link our Adjust account.
:::
- **Your own Adjust account.**
------------------------
## Integration Steps
To connect with Adjust, follow the steps:
Step 1. Import Adjust
Complete all the steps from our [integration guide](../get-started).
Make sure to integrate Adjust distributed via Appodeal SDK.
Step 2. Contact Us
Contact our support team via live chat or via email [support@appodeal.com](mailto:support@appodeal.com) with the following information:
- The desired option.
- Links to the apps in store, which you want to connect.
- Traffic sources, where you are planning to run UA campaigns.
Support team will finish your Adjust integration from Appodeal side and
let you know.
:::info
Make sure you have all the following features on your account before the
connection:
- CSV uploads (from Business plan).
- Cost reporting (from Custom plan).
- Kpi-service (from Custom plan).
:::
Step 1. Import Adjust
Complete all the steps from our [integration guide](/ios/get-started).
Make sure to integrate Adjust distributed via Appodeal SDK.
Step 2. Add Your Adjust Account To Appodeal
Add your Adjust account to Appodeal [here](https://app.appodeal.com/integrations/user_acquisition).
You will need to enter your Account name and User Token from Adjust.
User Token - your Api Token on Adjust side.
You can check Raw Data export to Amazon s3 box if you have your AWS (if you don't have it leave this box unchecked).
Step 3. Add Your App On Adjust Side
1. Add the following information:
- App name;
- Platform (add your app bundle id);
- Store id;
- Reporting currency (USD is preferable);
2. Create your app
3. Go to all Settings → S2S Security → Create token & Activate S2S
Authentication
Save this S2S Security Token for the next step
Step 4. Turn On Adjust In Attribution Settings
Go to your app settings in your Appodeal account and choose **Attribution Settings**.
**Primary MMP Account** - your MMP account from where we can get attribution data.
**Secondary MMP Account** (optional) - this option is needed if you transfer from one MMP account to another or if you want to test two different MMP's.
**Raw Data Source** - the source of raw data.
For Primary MMP Account choose your Adjust account, you can leave Secondary MMP Account empty,
for Raw Data Source choose Amazon S3 Bucket (if you have one and if you have added it with your Adjust account)
or choose Global Callback(enabled by default).
For Attribution Platform choose Adjust, Adjust S2S Security Token
(can be copied from Adjust App Settings → S2S Security from the previous step ) and
Adjust App Token is in your app settings, choose Production for Adjust Environment.
Step 5. Add Global Callback On Adjust Side
Go to your Adjust account app settings → Raw Data Export → Real-Time
Callbacks → Add Global Callback (the one copied in Step 4)
Step 6. Create required events on Adjust side
If you use your own Adjust account, you need to add required events
according to this [guide](../advanced/event-tracking)
so that in-app purchases will work correctly.
Step 7. Set Up Traffic Sources On Adjust Side
In order to see data of your campaign, you need to set up your traffic
sources on Adjust side such as [Meta](https://help.adjust.com/en/article/skad-facebook-integration)
or [Google](https://help.adjust.com/en/article/skad-google-integration) for example.You can use
[this preset]() to check the statistics of your UA campaign.
Step 8. Turn On Ad Spend Tracking
In order to be able to see Ad Spend data make sure to complete the steps
from this [guide](https://help.adjust.com/en/article/ad-spend) and link your traffic source account to Adjust.
---
## Demo Application
You can use our **demo app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers
for analytics. It allows users to group by the fact of purchasing
in-apps. This will help you adjust the ads for such users or turn them
off if needed.
In order to track in-app purchases, please refer to [this guide](../advanced/event-tracking).
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## AppsFlyer(Services)
:::note Before the start
AppsFlyer is available for linking only with your own AppsFlyer account with Premium Plan.
Make sure you have the following features:
- [DataLocker](https://support.appsflyer.com/hc/en-us/articles/360000877538-Data-Locker-for-Advertisers).
Data Locker writes your report data to cloud storage for loading into your BI systems.
- [Master API](https://support.appsflyer.com/hc/en-us/articles/213223166-Using-Master-API-campaign-performance-KPIs).
Get selected LTV, activity, Protect360, and retention campaign performance KPIs by API, in CSV or JSON format.
Select 1 or more apps.
These features are available on **AppsFlyer Premium Plan**.
Contact our support team via live chat or via email [support@appodeal.com](mailto:support@appodeal.com) to enable
Attribution Settings needed in step 4, this feature is absolutely free.
:::
AppsFlyer is a mobile marketing, analytics, and attribution platform.
With one connection of AppsFlyer you will be able to see all UA metrics
directly in our BI, without using MMP, analyze them in various sections,
and also get access to LTV forecasting.
Note that we also support [forecast metrics](/reporting/revenue-forecast), which will be available by
default with the current integration.
------------------------
## Integration Steps
To connect with AppsFlyer, follow the steps:
Step 1. Import AppsFlyer
Complete all the steps from our [integration guide](../get-started). Make sure to
integrate AppsFlyer distributed via Appodeal SDK.
Step 2. Add Your AppsFlyer Account
Add your AppsFlyer account to Appodeal here. You will need to enter:
- your Account name,
- Master API Token (can be found in your AppsFlyer account → API tokens [here](https://hq1.appsflyer.com/account/api-tokens)),
- data for Amazon s3 bucket, you can find it in [Datalocker](https://hq1.appsflyer.com/datalocker/overview).
Step 3. Set Up Datalocker
You need to set up Datalocker according to
[this guide](https://support.appsflyer.com/hc/en-us/articles/360000877538?utm_source=hq1&utm_medium=referral#set-up-data-locker)
on AppsFlyer side.
Make sure to indicate fields and report types.
Here is the required minimum for fields:
```text
• Advertising ID (advertising_id)
• Ad (af_ad)
• Ad ID (af_ad_id)
• Ad Type (af_ad_type)
• Adset Name (af_adset)
• Adset ID (af_adset_id)
• Attribution Lookback Window (af_attribution_lookback)
• Campaign ID (af_c_id)
• Channel (af_channel)
• Cost Currency (af_cost_currency)
• Cost Model (af_cost_model)
• Cost Value (af_cost_value)
• Keywords (af_keywords)• Partner (af_prt)
• Reengagement Window (af_reengagement_window)
• Site ID (af_siteid)
• Sub Param 1 (af_sub1)• Sub Param 2 (af_sub2)
• Sub Param 3 (af_sub3)
• Sub Param 4 (af_sub4)
• Sub Param 5 (af_sub5)
• Sub Site ID (af_sub_siteid)
• Web ID (af_web_id)
• Amazon Fire ID (amazon_aid)
• Android ID (android_id)
• App ID (app_id)
• App Name (app_name)
• App Version (app_version)
• AppsFlyer ID (appsflyer_id)
• Attributed Touch Time (attributed_touch_time)
• Attributed Touch Type (attributed_touch_type)
• Blocked Reason (blocked_reason)
• Blocked Reason Rule (blocked_reason_rule)
• Blocked Reason Value (blocked_reason_value)
• Blocked Sub Reason (blocked_sub_reason)
• Bundle ID (bundle_id)
• Campaign (campaign)
• Carrier (carrier)
• City (city)
• Contributor1 Partner (contributor_1_af_prt)
• Contributor1 Campaign (contributor_1_campaign)
• Contributor1 Match Type (contributor_1_match_type)
• Contributor1 Media Source (contributor_1_media_source)
• Contributor1 Touch Time (contributor_1_touch_time)
• Contributor1 Touch Type (contributor_1_touch_type)
• Contributor2 Partner (contributor_2_af_prt)
• Contributor2 Campaign (contributor_2_campaign)
• Contributor2 Match Type (contributor_2_match_type)
• Contributor2 Media Source (contributor_2_media_source)
• Contributor2 Touch Time (contributor_2_touch_time)
• Contributor2 Touch Type (contributor_2_touch_type)
• Contributor3 Partner (contributor_3_af_prt)
• Contributor3 Campaign (contributor_3_campaign)
• Contributor3 Match Type (contributor_3_match_type)
• Contributor3 Media Source (contributor_3_media_source)
• Contributor3 Touch Time (contributor_3_touch_time)
• Contributor3 Touch Type (contributor_3_touch_type)
• Country Code (country_code)
• Custom Data (custom_data)
• Customer User ID (customer_user_id)
• Deeplink URL (deeplink_url)
• Device Category (device_category)
• Device Download Time (device_download_time)
• Device Type (device_type)
• DMA (dma)
• Event Name (event_name)
• Event Revenue (event_revenue)
• Event Revenue Currency (event_revenue_currency)
• Event Revenue USD (event_revenue_usd)
• Event Source (event_source)
• Event Time (event_time)
• Event Value (event_value)
• Google Play Broadcast Referrer (gp_broadcast_referrer)
• Google Play Click Time (gp_click_time)
• Google Play Install Begin Time (gp_install_begin)
• Google Play Referrer (gp_referrer)
• HTTP Referrer (http_referrer)
• IDFA (idfa)
• IDFV (idfv)
• IMEI (imei)
• Adrevenue Impressions (impressions)
• Install App Store (install_app_store)
• Install Time (install_time)
• IP (ip)
• Is Primary Attribution (is_primary_attribution)
• Is Receipt Validated (is_receipt_validated)
• Is Retargeting (is_retargeting)
• Keyword Match Type (keyword_match_type)
• Language (language)
• Match Type (match_type)
• Media Source (media_source)
• Adrevenue Mediation Network (mediation_network)
• Adrevenue Network (monetization_network)
• Network Account ID (network_account_id)
• OAID (oaid)
• Operator (operator)
• Original URL (original_url)
• OS Version (os_version)
• Adrevenue Placement (placement)
• Platform (platform)
• Postal Code (postal_code)
• Region (region)
• Retargeting Conversion Type (retargeting_conversion_type)
• SDK Version (sdk_version)
• Adrevenue Segment (segment)
• State (state)
• User Agent (user_agent)
• Web Event Type (web_event_type)
• WIFI (wifi)
```
Here is the required minimum for report types:
Step 4. Turn On AppsFlyer In Attribution Settings
Go to your app settings in your Appodeal account and choose **Attribution
Settings**.
**Primary MMP Account** - your MMP account from where we can get attribution
data
**Secondary MMP Account** (optional) - this option is needed if you
transfer from one MMP account to another or if you want to test two
different MMP's
For Primary MMP Account choose your AppsFlyer account,
you can leave Secondary MMP Account empty.
For Attribution Platform choose AppsFlyer, Dev Key can be found in your
app settings on AppsFlyer side and App ID is on the top of the page in
the browser link when you choose app in your AppsFlyer account.
Step 5. Set Up Traffic Sources
In order to see data of your campaign you need to setup your traffic
sources such as [Meta](https://support.appsflyer.com/hc/en-us/articles/207033826-Facebook-Ads-integration-setup)
or [Google](https://support.appsflyer.com/hc/en-us/articles/115002504686-Google-Ads-AdWords-integration-setup),
for example. You can use [this preset]() to
check the statistics of your UA campaign.
Step 6. Send Ad Revenue Data To AppsFlyer (Optional)
If you want to send ad revenue data to AppsFlyer, then you need to
complete the following steps:
- Contact us via email [support@appodeal.com](mailto:support@appodeal.com) or the live chat and we will
enable ad revenue sending
- Complete the steps from this [guide](https://support.appsflyer.com/hc/en-us/articles/360004404977-Appodeal-campaign-configuration-)
and make sure to enable "Get Ad Revenue
Data" in **Configuration** → **Integrated Partners → Appodeal → Ad
Revenue**.
Step 7. Set Up Deep Linking Powered By OneLink (optional)
OneLink allows you to create thousands of links easily.
You can create
links with attribution, redirection, and deep linking capabilities that
convert paid users into app users, regardless of device, operating
system, platform and etc.
Please use this [guide](https://support.appsflyer.com/hc/en-us/articles/115005248543-Customer-experience-and-deep-linking-overview) to setup Deep Linking.
:::note
If you have any questions while integrating, feel free to contact us via
email [support@appodeal.com](mailto:support@appodeal.com) or the live chat.
:::
--------
## Demo Application
You can use our **demo app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers
for analytics. It allows to group users by the fact of purchasing
in-apps. This will help you adjust the ads for such users or simply turn
it off, if needed.
In order to track in-app purchases please refer to [this
guide](./../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Firebase(Services)
Firebase SDK (firebase-analytics and firebase-config) is used for
analytics and remote config for tests and settings.
------------------------
## Firebase Connection
To connect your Firebase account, follow the steps below.
### Step 1. Import Firebase
Firebase SDK is already included in Appodeal SDK (firebase-analytics and
firebase-config). You don't need to install it separately.
Complete all the steps from our [integration guide](../get-started). Make sure to integrate Firebase
distributed via Appodeal SDK.
### Step 2. Configure Firebase App
1. Follow this [guide](https://firebase.google.com/docs/ios/setup) to configure your Firebase app.
2. Add your GoogleService-Info.plist file from the Firebase console
to the root of your Xcode project. If prompted, select to add the
config file to all targets.
### Step 3. Set Up Firebase Remote Config In Attribution Settings (Optional)
If you want to use Firebase Remote Config in your app, you can add your
Firebase parameter keys from Firebase console -> Project name ->
Remote Config to Firebase Config Keys in Attribution Settings.
### Step 4. Enable Firebase Tracking In Attribution Settings
To enable sending events to Firebase SDK, you need to enable Firebase
Tracking in Attribution Settings.
------------------------
## Demo Application
You can use our **demo app **as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Meta(Services)
Meta SDK (facebook-core) is used for UA (User Acquisition).
:::note
If you are integrating Meta to see UA metrics in our Dashboard, it will work only in connection with Adjust/AppsFlyer.
To connect them, follow [this guide](adjust) for Adjust and [this guide](appsflyer) for AppsFlyer.
:::
## Meta connection
To connect Meta, follow the steps below.
### Step 1. Import Meta
Meta SDK is already included in Appodeal SDK (facebook-core). You don't
need to install it separately.
### Step 2. Configure Meta App
1. Follow this [guide](https://developers.facebook.com/docs/app-events/getting-started-app-events-ios) to configure you Meta app.
2. Make sure to complete step 5 from this [guide](https://developers.facebook.com/docs/app-events/getting-started-app-events-ios) and configure your project, you can skip step 6.
### Step 3. Enable Meta Tracking In Attribution Settings
1. You need to go to your app settings in your Appodeal account and
choose Attribution Settings.
2. In Meta Settings enable Meta Tracking.
--------
## Demo Application
You can use our **demo app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Using Services In Passive Mode(Services)
Appodeal SDK already includes services such as Adjust, AppsFlyer, and
Firebase, and we initialize them automatically with Appodeal
initialization.
If you want to be able to initialize services and use their methods
yourself, then you need to follow the steps below.
In order to use services in passive mode, you need to contact our
support team so that we make the necessary settings on our side.
You can find the details in the step below.
## Contact Us
Contact our support team via live chat or email support@appodeal.com
with the following information:
- Links to the apps in the store where you want to initialize
Adjust/AppsFlyer/Firebase on your own.
## Integrate Adjust, AppsFlyer, And Firebase
Complete all the steps from our integration guide, and make sure to
include services in your build.
Complete basic integration steps for Adjust, AppsFlyer, and Firebase.
## Initialize Adjust
After you have contacted our support team and got confirmation to go
further, you can initialize Adjust on your own and use all its methods
according to the official
[documentation](https://help.adjust.com/en/article/get-started-ios-sdk#initialize-the-sdk).
It is recommended to initialize Adjust in the
`didFinishLaunchingWithOptions` method of your **AppDelegate**.
```swift showLineNumbers
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
initializeAdjust()
return true
}
// Initialize Adjust
func initializeAdjust () {
let yourAppToken = "YOUR_APP_TOKEN"
let environment = ADJEnvironmentProduction
let adjustConfig = ADJConfig(
appToken: yourAppToken,
environment: environment)
Adjust.appDidLaunch(adjustConfig)
}
```
```objc showLineNumbers
#import "Adjust.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self initializeAdjust];
return YES;
}
// Initialize Adjust
- (void)initializeAdjust {
NSString *yourAppToken = @"{YourAppToken}";
NSString *environment = ADJEnvironmentProduction;
*adjustConfig = [ADJConfig configWithAppToken:yourAppToken
environment:environment];
[Adjust appDidLaunch:adjustConfig];
}
```
When running tests you should ensure that your environment is set to
`ADJEnvironmentSandbox`.
Change this to `ADJEnvironmentProduction` before you submit your
application to the App Store.
## Initialize AppsFlyer
After you have contacted our support team and got confirmation to go
further, you can initialize AppsFlyer on your own and use all its
methods according to the official [documentation](https://dev.appsflyer.com/hc/docs/integrate-ios-sdk)
и [ad-revenue guide](https://dev.appsflyer.com/hc/docs/ad-revenue-2).
```swift showLineNumbers
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
initializeAppsFlyer()
return true
}
// Initialize AppsFlyer
func initializeAppsFlyer () {
AppsFlyerLib.shared().appsFlyerDevKey = "YOUR_AF_DEV_KEY"
AppsFlyerLib.shared().appleAppID = "YOUR_APPLE_APP_ID"
AppsFlyerLib.shared().start()
AppsFlyerAdRevenue.start()
}
```
```objc showLineNumbers
#import
#import
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self initializeAppsFlyer];
return YES;
}
// Initialize AppsFlyer
- (void)initializeAppsFlyer {
[[AppsFlyerLib shared] setAppsFlyerDevKey:@"YOUR_AF_DEV_KEY"];
[[AppsFlyerLib shared] setAppleAppID:@"YOUR_APPLE_APP_ID"];
[[AppsFlyerLib shared] start];
[AppsFlyerAdRevenue start];
}
```
## Initialize Firebase
After you have contacted our support team and got confirmation to go
further, you don't need to initialize Firebase, as this is already done
on our side.
Firebase analytics will work automatically, and you can use any methods
you want, from Firebase Analytics and Firebase Remote-Config.
If you want to use Firebase Remote-Config in your app, then you need to
set it up as shown below and in the official
[documentation](https://firebase.google.com/docs/remote-config/get-started?platform=ios#swift)
```swift showLineNumbers
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
initializeFirebase()
return true
}
// Initialize Firebase
func initializeFirebase () {
var remoteConfig = RemoteConfig.remoteConfig()
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings
}
```
```objc showLineNumbers
#import
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self initializeFirebase];
return YES;
}
// Initialize Firebase
- (void)initializeFirebase {
self.remoteConfig = [FIRRemoteConfig remoteConfig];
FIRRemoteConfigSettings *remoteConfigSettings = [[FIRRemoteConfigSettings alloc] init];
remoteConfigSettings.minimumFetchInterval = 0;
self.remoteConfig.configSettings = remoteConfigSettings;
}
```
## Track In-app Purchases
You can track in-app purchase information and send info to Appodeal
servers for analytics. It allows users to group by the fact of
purchasing in-apps.
This will help you adjust the ads for such users or turn them off if
needed.
To track in-app purchases, please refer to this
[guide](../advanced/in-app-purchases).
## Event Tracking
Thanks to in-app events, you can track user activity inside your app.
You can keep track of events such as registration, passing levels,
purchases, etc., as in-app events.
The implementation of in-app events is mandatory for all post-install
analysis purposes.
You can send events to Adjust, AppsFlyer, and Firebase using the methods
from our documentation.
---
## App Privacy Details on the App Store
Starting December 8, 2020, app developers are required to provide
information about their Privacy Practices when submitting apps or
updated apps to the App Store. Privacy Practices are displayed to app
users in a concise nutrition label format on the app page in the app
store.
The table below describes the data types collected by the **Appodeal SDK
itself**, mapped to Apple's App Privacy taxonomy. Collection depends on your
integration and on end-user permissions — for example, location is collected
only when the app grants the relevant permission, and it is additionally
controlled by the SDK location-tracking setting (off by default). You, the
publisher, remain responsible for the final App Privacy declaration for your
app.
:::note
This page covers only the Appodeal SDK. Any ad network or service you enable
through Appodeal collects data under its own privacy declaration — account for
those separately when filling out your App Privacy details.
:::
## Data Types Collected by Appodeal SDK
Data type
Data collection
Remarks
Contact Info
Name
No
Don't collect
Email Address
No
Don't collect
Phone Number
No
Don't collect
Physical Address
No
Don't collect
Other User Contact Info
No
Don't collect
Health and Fitness
Health
No
Don't collect
Fitness
No
Don't collect
Financial Info
Payment Info
No
Don't collect
Credit Info
No
Don't collect
Other Financial Info
No
Don't collect
Location
Precise Location
Optional
Collected by the Appodeal SDK only if your app has been granted
the OS location permission. To opt out, do not request location
permission (omit the NSLocationWhenInUseUsageDescription
Info.plist key).
Coarse Location
Optional
Collected by the Appodeal SDK only if your app has been granted
the OS location permission. To opt out, do not request location
permission (omit the NSLocationWhenInUseUsageDescription
Info.plist key).
Sensitive Info
Sensitive Info
No
Don't collect
Contacts
Contacts
No
Don't collect
User Content
Emails or Text Messages
No
Don't collect
Photos or Videos
No
Don't collect
Audio Data
No
Don't collect
Gameplay Content
No
Don't collect
Customer Support
No
Don't collect
Other User Content
No
Don't collect
Browsing History
Browsing History
No
Don't collect
Search History
Search History
No
Don't collect
Identifiers
User ID
Optional
Collected only if the app provides a user ID via
Appodeal.setUserId(); not collected otherwise.
Device ID
Yes
Advertising ID (IDFA) / IDFV, collected for advertising targeting and
ad tracking.
Purchases
Purchase History
Optional
Collected only if the application passes purchase data to the Appodeal
SDK via Appodeal.trackInAppPurchase(...) /
validateAndTrackInAppPurchase(...).
Usage Data
Product Interaction
Yes
Ad interactions (impressions, clicks) collected for advertising.
Advertising Data
Yes
Collected for advertising targeting.
Other Usage Data
No
Don't collect
Diagnostics
Crash Data
No
Don't collect
Performance Data
Yes
Collected for advertising / analytics.
Other Diagnostic Data
No
Don't collect
Surroundings
Environment Scanning
No
Don't collect
Body
Hands
No
Don't collect
Head
No
Don't collect
Other Data
Other Data Types
Yes
The Appodeal SDK can also collect:
Technical device information (for example, device type, system
configuration information such as information about End Users operating
system, mobile browser (e.g., Firefox, Safari, and Chrome))
Other device information (e.g., whether users using a smartphone or
tablet and related information)
Network information (for example, network provider)
Carrier user ID (a number uniquely allocated to you by your network
provider)
---
## App Tracking Transparency
Starting in iOS 14.5, IDFA will be unavailable until an app calls the
[App Tracking Transparency](https://developer.apple.com/documentation/apptrackingtransparency)
framework to present the app-tracking
authorization request to the end-user. If an app does not present this
request, the IDFA will automatically be zeroed out, which may lead to a
significant loss in ad revenue.
To display the App Tracking Transparency authorization request for
accessing the IDFA, update your `Info.plist` to add the
`NSUserTrackingUsageDescription` key with a custom message describing
the usage.
``` xml
NSUserTrackingUsageDescriptionThis identifier will be used to deliver personalized ads to you.
```
And **AppTrackingTransparency.framework** to your project.
## Stack Consent Manager
Appodeal SDK provides a simple way to integrate App Tracking Transparency.
It will obtain request status and show ATT request if needed using Stack Consent Manager framework.
You need to enable corresponding message in your AdMob account. See [this guide](/advanced/google-cmp-and-tcfv2-support) for more details.
Consent Manager integration remains the
same as in the [GDPR/CCPA section](gdpr-and-ccpa).
Consent Manager will show ATT request only for users under **iOS 14.5**
or higher, you may want to add some notes in App Review Information
section of the app version page in App Store Connect. For example, it
can be something like: **App Tracking Transparency request is only
available for users under iOS 14.5 or higher.** This step may be needed
because Apple can reject builds that contain
AppTrackingTransparency.framework, but do not display ATT requests at
app launch.
## Manually
Call `requestTrackingAuthorizationWithCompletionHandler:` to present the
App Tracking Transparency authorization request alert. Call this method
at the application launch event. We recommend initializing Appodeal SDK
in the completion block.
```swift showLineNumbers
class AppDelegate : UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
ATTrackingManager.requestTrackingAuthorization() { status in
// Tracking authorization completed. Initialise Appodeal here.
}
return true
}
}
```
```objc showLineNumbers
#import
#import
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// Tracking authorization completed. Initialise Appodeal here.
}];
return YES;
}
```
-----------------
---
## COPPA(Data-protection)
For purposes of the [Children's Online Privacy Protection Act (COPPA)](http://business.ftc.gov/privacy-and-security/children%27s-privacy)
there is a setting called childDirectedTreatment. If your app is
designed for kids you can disable sending user data to ad networks by
calling the method below.
Should be called before the SDK initialization.
```swift showLineNumbers
Appodeal.setChildDirectedTreatment(true)
```
```objc showLineNumbers
[Appodeal setChildDirectedTreatment: YES]
```
:::info
Call `setChildDirectedTreatment()` method with `true` to indicate that you want your content treated as child-directed
for purposes of COPPA.
Call `setChildDirectedTreatment()` method with `false` to indicate that you don't want your content treated as
child-directed for purposes of COPPA.
:::
---------
---
## GDPR and CCPA(Data-protection)
:::info
Keep in mind that it’s best to contact qualified legal professionals, if you haven’t done so already, to get more
information and be well-prepared for compliance.
:::
[The General Data Protection Regulation](https://gdpr-info.eu/), better known as GDPR, took effect on May 25, 2018.
It's a set of rules designed to give EU citizens more control over their personal data.
Any *businesses established in the EU or with users based in Europe are required to comply with GDPR or risk facing heavy fines*.
The California Consumer Privacy Act (CCPA) went into effect on January 1, 2020.
**We have put together some guidelines to help publishers understand better the steps they need to take to be GDPR compliant.**
:::info You can learn more about GDPR and CCPA and their differences [here](https://iapp.org/resources/article/ccpa-and-gdpr-comparison-chart/).
:::
-------------
## Step 1. Update Privacy Policy
### Include Additional Information To Your Privacy Policy
Don’t forget to add information about IP address and advertising ID collection, as well as
[the link to Appodeal’s privacy policy](https://www.appodeal.com/privacy-policy)
to your app’s privacy policy on the App Store.
To speed up the process, you could use
[privacy policy generators](https://app-privacy-policy-generator.firebaseapp.com/) -
just insert advertising ID, IP address, and location (if you collect users’ location) in the **Personally Identifiable
Information you collect** field (in line with other information about your app) and
[the link to Appodeal’s privacy policy](https://www.appodeal.com/privacy-policy)
in the **Link to the privacy policy of third party service providers used by the app** field.
### Add A Privacy Policy To Your Mobile App
You must add your explicit privacy policies in two places: on your app’s Store Listing page and within your app.
You can find detailed instructions on adding your privacy policy to your app on legal service websites.
For example, Iubenda, the solution tailored to legal compliance, provides
[a comprehensive guide](https://www.iubenda.com/en/help/401-privacy-policy-for-ios-and-macos-apps)
on including a privacy policy in your app.
Make sure that your privacy policy website has an SSL certificate—this point might seem obvious,
but it’s still essential.
Here are two useful resources that you can utilize while working on your app compliance:
- [Privacy, Security and Deception regulations (by Google Play)](https://play.google.com/intl/en-GB_ALL/about/privacy-security-deception/user-data)
- [Recommendations on Developing a Meaningful Privacy Policy (by Attorney General California Department of Justice)](https://oag.ca.gov/sites/all/files/agweb/pdfs/cybersecurity/making_your_privacy_practices_public.pdf)
:::note
Please note that although we’re always eager to back you up with valuable information, we’re not authorized
to provide any legal advice. It’s important to address your questions to lawyers who specialize in this area.
:::
-------------
## Step 2. Configure Stack Consent Manager with TCF v2 Support
:::info
Since `Appodeal SDK 3.2.1` it is fully compatible with Google UMP and supports IAB TCF v2.
:::
In order for Appodeal and our ad providers to deliver ads that are more relevant to your users, as a mobile app publisher,
you need to collect explicit user consent in the regions covered by GDPR.
To get consent for collecting personal data of your users, we suggest you use a ready-made solution -
Stack Consent Manager based on **Google User Messaging Platform (UMP)**.
:::note Configure Google UMP
Before you start, you need to configure Google UMP. Follow [this instruction](/advanced/google-cmp-and-tcfv2-support) to setup a consent form.
:::
-------------
## Step 3. Integrate Stack Consent Manager
Stack Consent Manager comes with a pre-made consent window that you can easily present to your users.
That means you no longer need to create your own consent window.
:::info Starting from Appodeal SDK 3.0, Stack Consent Manager is included by default.
**Consent will be requested automatically on SDK initialization**, and consent form will be shown if it is
necessary without any additional calls.
Please keep in mind that Consent will be shown only in the **EU** region, you can use VPN for testing.
:::
This means that Appodeal SDK integration code remains the same:
```swift showLineNumbers
@UIApplicationMain
final class MyAppDelegate: UIResponder, UIApplicationDelegate, AppodealInitializationDelegate {
func application(
_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool {
Appodeal.setAutocache(false, types: .interstitial)
Appodeal.setLogLevel(.verbose)
// New optional delegate for initialization completion
Appodeal.setInitializationDelegate(self)
/// Any other pre-initialization
/// app specific logic
Appodeal.initialize(
withApiKey: "APP_KEY",
types: .interstitial
)
return true
}
func appodealSDKDidInitialize() {
// Appodeal SDK did complete initialization
}
}
```
```objc showLineNumbers
@interface MyAppDelegate ()
@end
@implementation MyAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Appodeal setAutocache:NO types:AppodealAdTypeInterstitial];
[Appodeal setLogLevel:APDLogLevelVerbose];
// New optional delegate for initialization completion
[Appodeal setInitializationDelegate:self];
/// Any other pre-initialization
/// app specific logic
[Appodeal initializeWithApiKey:@"APP KEY" types:AppodealAdTypeInterstitial];
return YES;
}
- (void)appodealSDKDidInitialize {
// Appodeal SDK did complete initialization
}
@end
```
-----------------
## Advanced
Starting from **Appodeal SDK 3.2.1** you do not have to update user consent manually.
Appodeal SDK support the iAB TCFv2 protocol. All consent data will be read from **NSUserDefaults** and
passed everywhere you may need. Even if you want to use an alternative solution for User Consent Management,
Appodeal SDK will read and not modify the consent data.
### Manual Consent Management
If you wish, you can manage and update consent manually using Stack Consent Manager calls.
:::info
Now **StackConsentManager** also supports **Swift Concurrency**.
:::
Consent manager SDK can be synchronized and shown at any moment of application lifecycle.
We recommend to synchronize it at application launch. Multiple synchronization calls are allowed.
Appodeal SDK will not show consent dialog if it has been presented.
For more details follow the example:
```swift showLineNumbers
/// Initialisation
class YourAppDelegate: AppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?
) -> Bool {
let parameters = ConsentUpdateRequestParameters(
appKey: "YOUR_APP_KEY",
mediationSdkName: "YOUR_SDK_NAME",
mediationSdkVersion: "YOUR_SDK_VERSION",
COPPA: true
)
// requesting consent info update
ConsentManager.shared.requestConsentInfoUpdate(parameters: parameters) { error in
guard error == nil else { return } // error occured while receiving consent info
// loading and showing consent dialog
ConsentManager.shared.loadAndPresentIfNeeded(rootViewController: UIViewController()) { error in
if let error {
// error occured
} else {
// everything was fine, now you have user's consent
// initialize SDK here
}
}
}
return true
}
}
```
```objc showLineNumbers
#import
@implementation YourAppDelegate
/// Initialisation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
APDConsentUpdateRequestParameters *parameters = [[APDConsentUpdateRequestParameters alloc] initWithAppKey:@"YOUR_APP_KEY" mediationSdkName:@"YOUR_SDK_NAME" mediationSdkVersion:@"YOUR_SDK_VERSION" COPPA:true];
// requesting consent info update
[APDConsentManager.shared requestConsentInfoUpdateWithParameters:parameters completion:^(NSError * error) {
if (error) {
// error occured while receiving consent info
return;
}
// loading and showing consent dialog
[APDConsentManager.shared loadAndPresentIfNeededWithRootViewController:[UIViewController new] completion:^(NSError *error) {
if (error) {
return; // error occured while receiving user consent
}
// everything was fine, now you have user's consent
// initialize SDK here
}];
}];
return YES;
}
@end
```
-----------------
### Force Present Consent Dialog
If you want to have more control over Consent dialog, you can use the following code.
Here you load Consent dialog separately and can store a reference to it or do whatever you need.
```swift showLineNumbers
/// Initialisation
class YourAppDelegate: AppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?
) -> Bool {
let parameters = ConsentUpdateRequestParameters(
appKey: "YOUR_APP_KEY",
mediationSdkName: "YOUR_SDK_NAME",
mediationSdkVersion: "YOUR_SDK_VERSION",
COPPA: true
)
// requesting consent info update
ConsentManager.shared.requestConsentInfoUpdate(parameters: parameters) { error in
guard error == nil else { return } // error occured while receiving consent info
// loading consent dialog
ConsentManager.shared.load { dialog, error in
guard error == nil else { return } // error occured while loading consent dialog
// showing consent dialog
dialog?.present(rootViewController: UIViewController(), completion: { error in
guard error == nil else { return } // error occured while receiving user consent
// everything was fine, now you have user's consent
// initialize SDK here
})
}
}
return true
}
}
```
```objc showLineNumbers
/// Initialisation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
APDConsentUpdateRequestParameters *parameters = [[APDConsentUpdateRequestParameters alloc] initWithAppKey:@"YOUR_APP_KEY" mediationSdkName:@"YOUR_SDK_NAME" mediationSdkVersion:@"YOUR_SDK_VERSION" COPPA:true];
// requesting consent info update
[APDConsentManager.shared requestConsentInfoUpdateWithParameters:parameters completion:^(NSError * error) {
if (error) {
return; // error occured while receiving consent info
}
// loading consent dialog
[APDConsentManager.shared loadWithCompletion:^(APDConsentDialog *dialog, NSError *error) {
if (error) {
return; // error occured while loading consent dialog
}
if (dialog) {
// showing consent dialog
[dialog presentWithRootViewController:[UIViewController new] completion:^(NSError *error) {
if (error) {
return; // error occured while receiving user consent
}
// everything was fine, now you have user's consent
// initialize SDK here
}];
}
}];
}];
return YES;
}
```
`YOUR_APP_KEY` is required parameter (Appodeal APP Key)
:::info
SDK only allows calling consent window api after synchronization
:::
-----------------
### Check Consent Status
After synchronization completion, you can receive information about the previous user consent.
Before synchronization this parameter is `undefined`
```swift showLineNumbers
// Check consent status
let status = ConsentManager.shared.status
```
```objc showLineNumbers
// Check consent status
APDConsentStatus status = [APDConsentManager.shared status];
```
-----------------
### Revoke User Consent
If you need to revoke user consent, you can use the following code:
```swift showLineNumbers
// Check consent status
ConsentManager.shared.revoke()
```
```objc showLineNumbers
// Check consent status
[APDConsentManager.shared revoke];
```
-----------------
### US State Regulations Support (Privacy Entry Point)
:::info Available since `StackConsentManager 4.0.0` (Appodeal SDK 4.2.0).
:::
US state privacy laws (CCPA, CPA, VCDPA, and others) follow an **opt-out model**: data
processing is allowed by default, but users must be given a permanent way to opt out — typically a
**"Do Not Sell or Share My Personal Information"** button (the *Privacy Entry Point*). In the US zone
the standard `loadAndPresentIfNeeded` flow does **not** show any form, because consent is not
required at launch — the opt-out form must be shown on demand, in response to a user tap.
To support this, Stack Consent Manager exposes two new APIs:
- `privacyOptionsRequirementStatus` — tells you whether you must surface a Privacy Entry Point
button in your app UI.
- `showPrivacyOptionsForm(rootViewController:completion:)` — shows the US opt-out form (or the
GDPR re-consent form when called in the EEA).
Both APIs become available after `requestConsentInfoUpdate` completes.
#### Check whether a Privacy Entry Point is required
Use `privacyOptionsRequirementStatus` to decide whether to render the opt-out button. The status
returns `.required` for users in regulated US states and in the EEA (for GDPR re-consent),
`.notRequired` elsewhere, and `.unknown` before `requestConsentInfoUpdate` has completed.
```swift showLineNumbers
if ConsentManager.shared.privacyOptionsRequirementStatus == .required {
// Show a "Do Not Sell or Share My Personal Information" / Privacy Settings button
}
```
```objc showLineNumbers
if (APDConsentManager.shared.privacyOptionsRequirementStatus == APDPrivacyOptionsStatusRequired) {
// Show a "Do Not Sell or Share My Personal Information" / Privacy Settings button
}
```
#### Show the Privacy Options form
Call `showPrivacyOptionsForm` from the action handler of your Privacy Entry Point button.
This is the **only** way to display the US opt-out form, and it must be triggered by an explicit
user interaction — not at app launch.
```swift showLineNumbers
@IBAction func privacyOptionsButtonTapped() {
ConsentManager.shared.showPrivacyOptionsForm(rootViewController: self) { error in
if let error {
// handle error
} else {
// user finished interacting with the form
}
}
}
```
Swift Concurrency:
```swift showLineNumbers
try await ConsentManager.shared.showPrivacyOptionsForm(rootViewController: self)
```
```objc showLineNumbers
- (IBAction)privacyOptionsButtonTapped:(id)sender {
[APDConsentManager.shared showPrivacyOptionsFormWithRootViewController:self
completion:^(NSError *error) {
if (error) {
// handle error
} else {
// user finished interacting with the form
}
}];
}
```
:::note
Once the user interacts with the US opt-out form, Stack Consent Manager writes the corresponding
`IABGPP_GppSID` and `IABGPP_HDR_GppString` keys to `NSUserDefaults`, where ad networks read them.
Before the form has been shown at least once, these keys remain empty and ad networks may treat
the user as "no consent collected".
:::
### Non-Personalized Advertising
:::info Available since Appodeal SDK 4.3.0.
:::
Consent is enforced automatically — no extra integration is required.
If you want to request non-personalized advertising regardless of the resolved consent, call
`Appodeal.setNonPersonalized(true)`. This disables the collection of data used for ad personalization,
and a publisher-set value takes precedence over the consent resolved from the CMP.
Call it before `Appodeal.initialize(...)`.
This is relevant in several scenarios:
- **Age-restricted users (US).** US state laws (CCPA/CPRA in California, and similar laws in Virginia,
Colorado, Connecticut, and others) restrict selling or sharing the personal data of minors, and COPPA
adds stricter rules for children under 13. Use this flag alongside
[`setChildDirectedTreatment`](coppa) when you cannot determine the exact age but targeting must be
limited.
- **Users who declined personalized advertising** through your own consent flow, when they are not
subject to a specific regulation covered by the other APIs.
- **General opt-out** — a catch-all to suppress targeting signals when none of the more specific privacy
flags apply.
```swift showLineNumbers
Appodeal.setNonPersonalized(true)
```
```objc showLineNumbers
[Appodeal setNonPersonalized:YES];
```
---
## Ad Revenue Callbacks(Advanced)
Appodeal SDK allows you to get impression-level revenue data with Ad
Revenue Callbacks. This data includes information about network name,
revenue, ad type, etc.
The impression-level ad revenue data can be used then to share with your
mobile measurement partner of choice, such as [Firebase](../services/firebase), for all
supported networks.
If you have integrated Firebase, which is included in Appodeal SDK,
using this [guide](../services/firebase), then ad revenue data will be sent automatically, you
can read more about it [here](launching-troas#step-2-set-up-your-firebase-account) in Step 2.
:::info Minimum Requirements:
Appodeal SDK 3.0.1+
:::
## Callback Implementation
```swift showLineNumbers
extension YourViewController: UIViewController, AppodealAdRevenueDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Appodeal.setAdRevenueDelegate(self)
}
func didReceiveRevenue(forAd ad: AppodealAdRevenue) {
let parameters: [String: Any] = [
"network": ad.networkName,
"ad_unit": ad.adUnitName,
"placement": ad.placement,
"revenue_precision": ad.revenuePrecision,
"demand": ad.demandSource,
"currency": ad.currency,
"revenue": ad.revenue,
"ad_type": ad.adTypeString
]
}
}
```
```objc showLineNumbers
@interface YourViewController ()
@end
@implementation YourViewController
- (void)viewDidLoad {
[super viewDidLoad];
[Appodeal setAdRevenueDelegate:self];
}
- (void)didReceiveRevenueForAd:(id)ad {
NSDictionary *parameters = @{
@"network": ad.networkName,
@"ad_unit": ad.adUnitName,
@"placement": ad.placement,
@"revenue_precision": ad.revenuePrecision,
@"demand": ad.demandSource,
@"currency": ad.currency,
@"revenue": @(ad.revenue),
@"ad_type": ad.adTypeString
};
}
@end
```
:::note Admob Notice
To get impression-level ad revenue from Admob you also need to turn on the setting in your [AdMob account](https://apps.admob.com/v2/settings/account-info).
Go to your **Admob Account Settings** → **Account** → turn on **Impression-level ad revenue toggle**.
:::
------------------
## Appodeal Ad Revenue Description
`AppodealAdRevenue` - represents revenue information from the ad network.
| Parameter | Type | Description |
|------------------|-----------------|-----------------------------------------------------------------------------------------------------------------|
| networkName | String | The name of the ad network. |
| demandSource | String | The demand source name and bidder name in case of impression from real-time bidding |
| adUnitName | String | Unique ad unit name. |
| placement | String | Appodeal's placement name. |
| revenue | Double | The ad's revenue amount or 0 if it doesn't exist. |
| adType | Int | Appodeal's ad type. |
| adTypeString | String | Appodeal's ad type as string presentation. |
| platform | String | Appodeal's platform name. |
| currency | String | Current currency supported by Appodeal (USD) as string presentation. |
| revenuePrecision | String | The revenue precision.
:::info Revenue Precision options
1. `exact` - programmatic revenue is the resulting price of the auction
2. `publisher_defined` - revenue from crosspromo campaigns
3. `estimated` - revenue based on ad network pricefloors or historical eCPM
4. `undefined` - revenue amount is not defined
:::
## Use Case
:::info Please remember:
If you have integrated analytics for example Firebase using this
[guide](../services/firebase) with Appodeal, then no additional steps are
required.
:::
In case you are using your own analytics in the project, please find the
example below:
```swift showLineNumbers
extension YourViewController: UIViewController, AppodealAdRevenueDelegate {
override func viewDidLoad() {
super.viewDidLoad()
Appodeal.setAdRevenueDelegate(self)
}
func didReceiveRevenue(forAd ad: AppodealAdRevenue) {
let parameters: [String: Any] = [
"network": ad.networkName,
"ad_unit": ad.adUnitName,
"placement": ad.placement,
"revenue_precision": ad.revenuePrecision,
"demand": ad.demandSource,
"currency": ad.currency,
"revenue": ad.revenue,
"ad_type": ad.adTypeString
]
//AppsFlyer
let adRevenueParams:[AnyHashable: Any] = [
kAppsFlyerAdRevenueAdUnit : ad.adUnitName,
kAppsFlyerAdRevenueAdType : ad.adTypeString
]
AppsFlyerAdRevenue.shared().logAdRevenue(
monetizationNetwork: ad.networkName,
mediationNetwork: MediationNetworkType.appodeal,
eventRevenue: ad.revenue,
revenueCurrency: ad.currency,
additionalParameters: adRevenueParams
)
//Adjust
let adRevenue = ADJAdRevenue(source: ADJAdRevenueSourcePublisher)
adRevenue.setRevenue(ad.revenue, currency: ad.currency)
adRevenue.adRevenueUnit(ad.adUnitName)
adRevenue.adRevenueNetwork(ad.networkName)
Adjust.trackAdRevenue(adRevenue)
//Firebase
Analytics.logEvent(AnalyticsEventAdImpression, parameters: [
AnalyticsParameterAdFormat: ad.adTypeString,
AnalyticsParameterAdSource: ad.networkName,
AnalyticsParameterAdUnitName: ad.adUnitName,
AnalyticsParameterAdCurrency: ad.currency,
AnalyticsParameterValue: ad.revenue
])
}
}
```
```objc showLineNumbers
@interface YourViewController ()
@end
@implementation YourViewController
- (void)viewDidLoad {
[super viewDidLoad];
[Appodeal setAdRevenueDelegate:self];
}
- (void)didReceiveRevenueForAd:(id)ad {
NSDictionary *parameters = @{
@"network": ad.networkName,
@"ad_unit": ad.adUnitName,
@"placement": ad.placement,
@"revenue_precision": ad.revenuePrecision,
@"demand": ad.demandSource,
@"currency": ad.currency,
@"revenue": @(ad.revenue),
@"ad_type": ad.adTypeString
};
//AppsFlyer
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
dictionary[kAppsFlyerAdRevenueAdUnit] = ad.adUnitName
dictionary[kAppsFlyerAdRevenueAdType] = ad.adTypeString
[[AppsFlyerAdRevenue shared] logAdRevenueWithMonetizationNetwork:ad.networkName
mediationNetwork:AppsFlyerAdRevenueMediationNetworkTypeAppodeal
eventRevenue:ad.revenue
revenueCurrency:ad.currency
additionalParameters:dictionary];
//Adjust
ADJAdRevenue *adRevenue = [ADJAdRevenue alloc initWithSource: ADJAdRevenueSourcePublisher];
[adRevenue setRevenue:ad.revenue currency:ad.currency];
[adRevenue setAdRevenueUnit:ad.adUnitName];
[adRevenue setAdRevenueNetwork:ad.networkName];
[Adjust trackAdRevenue:adRevenue];
//Firebase
[FIRAnalytics logEventWithName:kFIREventAdImpression
parameters:@{
kFIRParameterAdFormat:ad.adTypeString,
kFIRParameterAdSource:ad.networkName,
kFIRParameterAdUnitName:ad.adUnitName,
kFIRParameterCurrency:ad.currency,
kFIRParameterValue:ad.revenue,
}];
}
@end
```
------------------
---
## Ad Revenue Forwarding to MMP/BI(Advanced)
Appodeal SDK allows you to get ad revenue data using
[Ad Revenue Attribution API](../../advanced/ad-revenue-attribution)
and [Ad Revenue Callbacks](ad-revenue-callback).
This data includes information about network name, revenue, ad type,
etc.
It is possible to send ad revenue data to Adjust, AppsFlyer, and also to
your own MMP/BI.
To send ad revenue data to MMP/BI, please follow the steps below:
## Adjust
No additional steps are required if you have integrated Adjust using this [guide](../services/adjust).
Ad revenue data will be sent automatically after the ad impression.
If you want to send ad revenue data to Adjust you need to use the code below :
```
let adRevenue = ADJAdRevenue(source: ADJAdRevenueSourcePublisher)
adRevenue.setRevenue(ad.revenue, currency: ad.currency)
adRevenue.adRevenueUnit(ad.adUnitName)
adRevenue.adRevenueNetwork(ad.networkName)
Adjust.trackAdRevenue(adRevenue)
```
## AppsFlyer
**Ad Revenue Attribution API**:
- If you want to send ad revenue to AppsFlyer, please follow step 6
from the [AppsFlyer](../services/appsflyer) guide.
**Ad Revenue Callbacks**:
- Please refer to the [guide](ad-revenue-callback) in this
case.
## Own MMP/BI
**Ad Revenue Attribution API**:
- Contact us via email at [support@appodeal.com](mailto:support@appodeal.com) or the live chat, and we
will enable ad revenue sending
- Send your attribution ID to Appodeal using `Appodeal.setExtraData`
method from this [guide](user-data)
- To get ad revenue data, you need to follow this
[guide](../../advanced/ad-revenue-attribution)
- Then you can send received ad revenue data to your MMP/BI
**Ad Revenue Callbacks**:
- Please refer to the [guide](ad-revenue-callback) in this
case.
---
## Configure Mediated Networks(Advanced)
Select the ad types that are used in your application and the ad networks
that you want to include, and select services if you use **Appodeal SDK Full Package**.
Copy the resulting configuration into your project's `Podfile`.
---
## Event Tracking(Advanced)
## Introduction
Thanks to in-app events, you can track user activity inside your app.
You can keep track of events such as registration, passing levels,
purchases, etc., as in-app events. The implementation of in-app events
is mandatory for all post-install analysis purposes.
## Types Of Events
In-app events can be divided into two categories:
- **Basic in-app events** are standard in-app events that help you
understand user activity inside your app.
**Examples:**
``` text
level1_finished
level2_start
app_login
```
- **Rich in-app events** are the same as basic in-app events but let you
get more detailed information about the event through a number of
parameters. You will learn more about them in step 1. Through
parameters, you can send additional information about the event. For
example, you can not only learn that app was opened but also the
exact date and time.
**Examples:**
``` text
level1_finished (result)
level2_start(time)
app_login(date)
```
## Recommended Events
You need to select the events that best suit your application.
:::info Recommendations:
- For better navigation through reports, we recommend using the same
event names in your app across all platforms.
- Create all kinds of events with a maximum number of details that
describe user actions in your application.
- We recommend using only lower-case alpha-numeric characters (a-z and
0-9) for your in-app event names.
:::
**Examples (for other apps):** Expand source
``` text
appodeal_initialized
complete_registration
user_login
tutorial_completion
on_search
content_view
in_app_purchase
```
**Examples (for games):** Expand source
``` text
game_start
game_win
game_end
main_menu_open
game_lose
round_start
round_end
pause_menu_open
design_dialog_open
settings_dialog_open
design_application_changed
level1_complete
appodeal_consent_dialog_open
appodeal_consent_dialog_result
```
# Step 1. How To Track In-app Events
Appodeal SDK allows you to send events to the following analytic
services using a single method:
- [Firebase](../services/firebase)
- [AppsFlyer](../services/appsflyer)
- [Adjust](../services/adjust)
- [Meta](../services/meta)
Use this method for send event for all connected services:
```swift showLineNumbers
Appodeal.trackEvent(
"some event",
customParameters: ["foo": "bar"]
)
```
```objc showLineNumbers
[Appodeal trackEvent:@"some event"
customParameters:@{ @"foo": @"bar" }
];
```
Use this method for send event for a specific service:
```swift showLineNumbers
Appodeal.trackEvent(
"some event",
customParameters: ["foo": "bar"],
analytics: [.adjust, .firebase, .adjust, .facebook]
)
```
```objc showLineNumbers
[Appodeal trackEvent:@"some event"
customParameters:@{ @"foo": @"bar" }
analytics:(APDAnalyticsServiceAll)
];
```
------------------
:::info Please note:
Event parameters can only be strings and numbers, they allow you to send
additional information about the event in your app.
:::
# Step 2. Configure In-app Events
Some additional steps may be needed on the MMP side to complete events
setup.
## Appodeal Free Adjust Account
- If you want to send events to Adjust, contact our support team via
email [support@appodeal.com](mailto:support@appodeal.com) or in the live chat and send us the list
with event names.
By default, Appodeal SDK sends s2s events to Adjust.
The list of s2s events :
- dc_cpa_event_d0 - this event includes the ARPU of Day 0 after the
app install
- dc_cpa_event_d2 - this event includes the ARPU of Day 2 after the
app install
- dc_cpa_event_d7 - this event includes the ARPU of Day 7 after the
app install
- dc_cpa_event_d30 - this event includes the ARPU of Day 30 after the
app install
:::info
If you want to target those s2s events in your UA campaigns, please
contact our support team via email [support@appodeal.com](mailto:support@appodeal.com) or in the live
chat so we can connect those events with your Traffic Source.
:::
## Own Adjust Account
If you want to send events to Adjust you need to create your events on
Adjust side according to this [guide](https://help.adjust.com/en/article/basic-event-setup)
and **send their tokens** to us via email
[support@appodeal.com](mailto:support@appodeal.com) or in the live chat:
- Find your app in the dashboard and select your app options caret (^).
- Select **All Settings > Events**.
- Find the **Create New Event** label at the bottom of the module and enter your event name.
- Select **Create**.
- **Send us the token** of each event specifying the event name(you can find the token right next to the event in **All Settings > Events**)
You also need to create some required SDK events presented below :
**Required SDK events:**
``` text
hs_sdk_purchase
hs_sdk_unknown
hs_sdk_purchase_error
```
**hs_sdk_purchase** - in-app purchase was validated successfully
**hs_sdk_unknown** - unknown event
**hs_sdk_purchase_error** - in-app purchase wasn't validated, error
occurred
## Own AppsFlyer Account
- No additional steps are required
---
## In-App purchases(Advanced)
## Automatic verification and sending of purchase information
:::info
Starting Appodeal SDK 3.7.0+, it is possible to automatically verify and submit purchases/subscription to
Appodeal, as well as receive purchase information from the Appodeal SDK using [Appsflyer](../services/appsflyer).
:::
To activate this feature contact us via email at [support@appodeal.com](mailto:support@appodeal.com)
or the live chat, and ask to enable ad **ROI360** feature.
Before implementing the ROI360 needs to be integrated with AppStoreConnect.
To do this, you need to perform the following steps:
### Step 1. Set the App Store credentials for ROI360 receipt validation
Obtain the following credentials from App Store Connect and forward this information to our support team.
* In-App Purchase key
* Key ID
* Issuer ID
To set iOS credentials:
1. In the App Store Connect, go to **Users and Access**
2. Go to **Users and Access > Integrations**, and from the **Keys** list, select **In-App Purchase**.
3. Click **+** to generate a new In-App Purchase key.
4. Enter a name for your API key.
5. Click **Generate**.
6. Click **Download In-App Purchase Key** next to the key you just generated to download it. **Note:** You can only download the key once.
7. In App Store Connect, copy the **Key ID** of the key you just generated and paste it into the AppsFlyer purchases & subscriptions setting for **Key ID**.
8. In App Store Connect, copy the **Issuer ID**. Note:** If the **Issuer ID** is not displayed at the top of the page, create an App Store Connect API key (with any access level). After that, the Issuer ID will appear at the top of the page for the In-App Purchase key.
### Step 2. Send App Store notifications directly to AppsFlyer
Before continuing, make sure to request **the AppsFlyer server notifications endpoint** from our support team.
1. In **App Store Connect**, in the **App Information** section, scroll to **App Store Server Notifications**, and next to **Production Server URL**, click **Edit**.
2. Paste the URL provided by our support team, select **Version 2 Notifications**, and click **Save**.
3. In App Store Connect, in the App Information section, scroll to App Store Server Notifications, and next to **Sandbox Server URL**, click **Edit**.
4. Paste the URL copied from AppsFlyer, select **Version 2 Notifications**, and click **Save**.
### Optional. Step 3 Setting up AppodealPurchaseCallback
Once the functionality is enabled and configured, when a purchase is made in your app, Appodeal SDK will automatically detect, verify and send the inApps/subscription details to Appodeal Dashboard. If you want to receive purchase information in your app, you need to perform the following steps:
1. Set the purchase delegate. We recommend doing it in the **AppDelegate** `-didFinishLaunchingWithOptions:` function:
```swift showLineNumbers
@UIApplicationMain
final class MyAppDelegate: UIResponder, UIApplicationDelegate, AppodealInitializationDelegate {
func application(
_ application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
Appodeal.setLogLevel(.verbose)
// New optional delegate for initialization completion
Appodeal.setInitializationDelegate(self)
// highlight-start
// Optional delegate for for ROI360
Appodeal.setPurchaseDelegate(self)
// highlight-end
/// Any other pre-initialization
/// app specific logic
Appodeal.initialize(
withApiKey: "APP_KEY",
types: .interstitial
)
return true
}
}
```
```objc showLineNumbers
@interface MyAppDelegate ()
@end
@implementation MyAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Appodeal setLogLevel:APDLogLevelVerbose];
// Optional delegate for initialization completion
[Appodeal setInitializationDelegate:self];
// highlight-start
// Optional delegate for for ROI360
[Appodeal setPurchaseDelegate:self];
// highlight-end
/// Any other pre-initialization
/// app specific logic
[Appodeal initializeWithApiKey:@"APP KEY" types:AppodealAdTypeInterstitial];
return YES;
}
@end
```
2. Implement the following methods of the AppodealPurchaseDelegate protocol:
```swift showLineNumbers
extension AppDelegate: AppodealPurchaseDelegate {
func didReceivePurchase(_ successPurchases: [String: Any]?) {
print("[ROI360] successPurchases: ", successPurchases)
}
func didFailPurchase(_ error: (any Error)?) {
print("[ROI360] failPurchases: ", error?.localizedDescription)
}
}
```
```objc showLineNumbers
@interface AppDelegate ()
@end
@implementation AppDelegate
- (void)didReceivePurchase:(NSDictionary * _Nullable)successPurchases {
NSLog(@"[ROI360] successPurchases: %@", successPurchases);
}
- (void)didFailPurchase:(NSError * _Nullable)error {
NSLog(@"[ROI360] failPurchases: %@", error.localizedDescription);
}
@end
```
The purchase object contains the following information:
| Parameter | Description
|------------------------------------|----------------------------------------------------------------------------------------------------------|
| **product_id** | The identifier of the purchased product |
| **purchase_date** | The date and time when the purchase was made |
| **transaction_id** | The unique identifier for the transaction assigned by the App Store |
At this point, the connection of automatic purchases is fully completed.
:::note
Purchase reports will be automatically uploaded to the Dashboard of your personal cabinet 2 times a day
:::
## Manual verification and sending of purchase information
:::info
In-App purchase tracking will work only in connection with
Adjust/AppsFlyer. To connect them, follow [this guide](../services/adjust)
for Adjust and [this guide](../services/appsflyer) for
AppsFlyer.
:::
It's possible to track in-app purchase information and send info to
Appodeal servers for analytics. It allows to group users by the fact of
purchasing in-apps. This will help you to adjust the ads for such users
or simply turn it off, if needed. To make this setting work correctly,
please submit the purchase info via the Appodeal SDK.
### Step 1. Validate In-app Purchases
To make this work correctly, please submit the purchase information via
Appodeal SDK.
:::info
Please make sure to use **all** **the parameters** from the method below
and don't comment out any of them.
:::
```swift showLineNumbers
Appodeal.validateAndTrack(
inAppPurchase: "some product id",
type: .autoRenewableSubscription,
price: "9.99",
currency: "USD",
transactionId: "some transaction id",
additionalParameters: additionalParameters,
success: { [weak self] in self?.alert("Purchase is valid", message: $0.description) },
failure: { [weak self] error, _ in self?.alert("Purchase is invalid", message: error?.localizedDescription) }
)
```
```objc showLineNumbers
[Appodeal validateAndTrackInAppPurchase:@"some product id"
type:APDPurchaseTypeConsumable
price:@"9.99"
currency:@"USD"
transactionId:@"some transaction id"
additionalParameters:additionalParameters
success:^(NSDictionary *validationResult) {
NSLog(@"Purchase is valid: %@", validationResult);
} failure:^(NSError *error) {
NSLog(@"Purchase is invalid: %@", error);
}];
```
:::info
Please make sure if you have created in-app product in App Store
Connect to use:
- **.consumable** or **.nonConsumable** for purchase type,
- **.autoRenewableSubscription** or **.nonRenewingSubscription** for
subscription.
:::
Parameter
Description
Usage
inAppPurchase
some product id
Adjust/AppsFlyer
type
Type must be :
.consumable or .nonConsumable
.autoRenewableSubscription or .nonRenewingSubscription
Adjust/AppsFlyer
price
In-app event revenue.
Adjust/AppsFlyer/Appodeal
currency
In-app event currency.
Adjust/AppsFlyer/Appodeal
transactionId
some transaction id
Adjust/AppsFlyer
additionalParameters
Additional parameters of the in-app event.
:::info
If you are using **your own Adjust** account you need to complete Step 2
from our Event Tracking [guide](event-tracking) and create some required events on Adjust
side.
:::
### Step 2. Contact Us
After all completed steps contact our support team via email [support@appodeal.com](mailto:support@appodeal.com)
or a live chat with the following information :
1. Purchases implementation logic in your app (when and where you call
validate method and validate purchases).
2. Allow us to test purchases in your app and send us the testflight to
email [support@appodeal.com](mailto:support@appodeal.com).
### Step 3. Testing
After you have contacted our Support Team and provided all the required
information you can test your app to make sure purchases are validated.
1. Please go to your App Settings → Attribution Settings → and change
Adjust Environment from Production to **Sandbox** to be able to test
validation and don't forget to press **Save** at the end of the
page.
2. Connect your device to your computer with the opened console (iOS
Console) and tag logs by *purchase*
3. Now you can open your App and make a test purchase, if you can
see **Valid purchase** in the console, then
validation went successfully.
4. If validation has failed, then please recheck all the steps above.
5. After testing, change your Adjust Environment to **Production** in
App Settings → Attribution Settings.
---
## Launching a tROAS campaign in Google Ads(Advanced)
**tROAS (target Return On Ad Spend )** is Google's smart bidding
strategy that uses auction-time bidding to reach your specified value.
Target ROAS regulates bids to maximize the value of your conversions.
By simply integrating Appodeal SDK in your app, you will be able to send
ad revenue data to Firebase and launch a tROAS campaign in Google Ads.
:::info Minimum Requirements
Appodeal SDK 3.0.0+ with Firebase (included by default).
:::
## Step 1. Integrate Firebase
Complete all the steps from our
[Firebase integration guide](../services/firebase).
## Step 2. Set Up Your Firebase Account
:::info
Make sure you have admin access to your Firebase and Google Ads
accounts.
:::
1. You need to link your Firebase project with your Google Adwords
account. In order to do so, please go to your **Firebase project → Project
settings → Integrations → Google Ads → Link→ Choose your Google Ads
account**
2. You can use Google Analytics to measure ad revenue generated from
displaying ads.
To measure ad revenue, we are logging the **custom_ad_impression** event whenever your user sees an advertisement
in your app.
In Analytics, your most important events are called conversions, and in
order to be able to import them
to your Google Adwords in the next step, account you need to mark
the **custom_ad_impression** event as a conversion by going to
**your Firebase project → Analytics → Events.**
:::tip
Event reports are available within 24 hours on the Firebase side
:::
## Step 3. Set Up Your Google Adwords Account
Go to your Google Adwords account → Tools and Settings → Conversions →
New Conversions → Import → Google Analytics 4 properties →
App and import **first_open** and **custom_ad_impression** conversions.
Now you have set up everything and it is time to create a campaign on
Google Ads side.
Check [this guide](https://support.google.com/google-ads/answer/6268637?hl=en)
to learn more about tROAS bidding.
---
## Logging
## Console Logs
SDK logging allows you to check SDK integration and activity, including
information about waterfalls with ad units, ads requests, loading, and
some other. We recommend always enabling logs and using the debug logs
to get full SDK information.
Enable logging using the code below before SDK initialization:
```swift showLineNumbers
Appodeal.setLogLevel(.verbose)
```
```objc showLineNumbers
[Appodeal setLogLevel:APDLogLevelVerbose];
```
------------------
:::info
Should be called before the SDK initialization.
:::
Available parameters:
- `APDLogLeveloff` - logs off;
- `APDLogFlagError` - only error messages;
- `APDLogLevelWarning` - warning and error messages;
- `APDLogLevelDebug` - debug messages;
- `APDLogLevelInfo` - error, warning and information messages;
- `APDLogLevelVerbose` - all SDK and ad network messages.
Connect a device with the app installed, open the Xcode console, run the
app, and check SDK logs by the `"Appodeal"` tag. For more information
about the console, please visit [Debugging with Xcode](https://developer.apple.com/documentation/os/logging/viewing_log_messages).
Here is an example of Appodeal IOS SDK for interstitial ad type. Please
note, logs can be different if you use another ad type or a different
SDK configuration.
``` c#
//Information about sdk initialization
[Appodeal 3.0.2] [info] [application] SDK was running on simulator
[Appodeal 3.0.2] [debug] [services] Initialize Stack Analytics service with parameters:
[Appodeal 3.0.2] [debug] [services] Complete services manager initialization
//Default configuration for banner
[Appodeal 3.0.2] [debug] [impression] Banner APDAutolayoutBannerView 140615982580656 size: {320, 50} change size to {320, 50}
[Appodeal 3.0.2] [warning] [impression] Banner APDAutolayoutBannerView 140615982580656 size: {320, 50} unable to use smart sizing!
//Networks adapters and and their versions
[Appodeal 3.0.2] [info] [api] MRAID integration via SDK of version 2.0.4
[Appodeal 3.0.2] [info] [api] Crosspromo & Direct Offers integration via SDK of version 3.0.2
[Appodeal 3.0.2] [info] [api] IronSource integration via SDK of version 7.2.6
[Appodeal 3.0.2] [info] [api] AdColony integration via SDK of version 4.9.0.0
[Appodeal 3.0.2] [info] [api] NAST integration via SDK of version 2.0.4
[Appodeal 3.0.2] [info] [api] AppLovin integration via SDK of version 11.6.1
[Appodeal 3.0.2] [info] [api] Vungle Ads integration via SDK of version 6.12.1
[Appodeal 3.0.2] [info] [api] BidMachine integration via SDK of version 2.0.0.5
[Appodeal 3.0.2] [info] [api] Unity Ads integration via SDK of version 4.5.0
[Appodeal 3.0.2] [info] [api] Meta Audience Network integration via SDK of version 6.12.0
[Appodeal 3.0.2] [info] [api] VAST integration via SDK of version 2.0.4
[Appodeal 3.0.2] [info] [api] MyTarget integration via SDK of version 5.17.2
[Appodeal 3.0.2] [info] [api] A4G integration via SDK of version afma-sdk-i-v9.14.0
[Appodeal 3.0.2] [info] [api] Yandex Mobile Ads integration via SDK of version 5.2.1/4.4.0
[Appodeal 3.0.2] [info] [api] Notsy integration via SDK of version afma-sdk-i-v9.14.0
[Appodeal 3.0.2] [info] [api] Google Mobile Ads integration via SDK of version afma-sdk-i-v9.14.0
//Mediation start
[Appodeal 3.0.2] [debug] [mediation] Starting APDInterstitialAdModule
[Appodeal 3.0.2] [debug] [mediation] Starting APDAdQueueManager for "Interstitial Ad" ad request
[Appodeal 3.0.2] [info] [mediation] Trying to fetch waterfall
[Appodeal 3.0.2] [info] [mediation] Mediation start for impression: C73CF384-3844-4D18-84EC-BEB33EDC923A
//Requesting process starts from the most expensive ad unit to the cheapest.
//SDK makes a request and, if network can’t return the ad (with result: No fill), SDK will continue to do requests until it gets an ad.(with result: Fill)
//Information and result of each requested ad unit you can find in the logs.
//Rewarded video ad unit from admob with eCPM 1000.0 is not loaded due to no fill from the network,
//SDK will continue to do requests, the next ad unit is admob with eCPM 70.0 etc. :
[Appodeal 3.0.2] [info] [mediation] Start to load Rewarded Video admob wo pricefloor admob_rewarded_video_1000.0 eCPM = 1000.000000
[Appodeal 3.0.2] [info] [mediation] Complete loading Rewarded Video admob wo pricefloor admob_rewarded_video_1000.0 eCPM = 1000.000000 with result: No fill
[Appodeal 3.0.2] [info] [mediation] Start to load Rewarded Video admob pricefloor admob_rewarded_video_70.0 eCPM = 70.000000
[Appodeal 3.0.2] [info] [mediation] Complete loading Rewarded Video admob pricefloor admob_rewarded_video_70.0 eCPM = 70.000000 with result: No fill
//The ad is loaded (fill):
[Appodeal 3.0.2] [info] [mediation] Start to load Interstitial Ad applovin wo pricefloor applovin_interstitial_0.7 eCPM = 0.700000
[Appodeal 3.0.2] [info] [mediation] Complete loading Interstitial Ad applovin wo pricefloor applovin_interstitial_0.7 eCPM = 0.700000 with result: Fill
[Appodeal 3.0.2] [debug] [mediation] Trying to proceed ad unit:Interstitial Ad backfill wo pricefloor mraid_interstitial eCPM = 0.001000
[Appodeal 3.0.2] [debug] [impression] Skip ad unit cause SDK already contains ad with eCPM: 0.70 higher than unit: Interstitial Ad backfill wo pricefloor mraid_interstitial eCPM = 0.001000
[Appodeal 3.0.2] [debug] [mediation] Break mediation
[Appodeal 3.0.2] [info] [mediation] Complete loading Interstitial Ad backfill wo pricefloor mraid_interstitial eCPM = 0.001000 with result: Break AdUnit
[Appodeal 3.0.2] [debug] [mediation] Mediation completed
[Appodeal 3.0.2] [info] [mediation] Mediation complete for impression: C73CF384-3844-4D18-84EC-BEB33EDC923A
//The ad is shown, finished, clicked and closed:
[Appodeal 3.0.2] [debug] [api] [Callback] [Interstitial] Did appear
[Appodeal 3.0.2] [debug] [impression] Impression succesfully tracked
[Appodeal 3.0.2] [debug] [api] [Callback] [Interstitial] Did click
[Appodeal 3.0.2] [debug] [impression] Click succesfully tracked
[Appodeal 3.0.2] [info] [impression] Track viewability finish for item: Interstitial Ad applovin wo pricefloor applovin_interstitial_0.7 eCPM = 0.700000
[Appodeal 3.0.2] [debug] [api] [Callback] [Interstitial] Did disappear
//By default auto cache is enabled, sdk starts to request ad units in the waterfall after the ad disappeared from the screen:
[Appodeal 3.0.2] [info] [impression] Prepare impression storage for reuse
[Appodeal 3.0.2] [info] [mediation] Mediation start for impression: D892B114-DDA5-4B31-BEF2-46DAD770C9B0
[Appodeal 3.0.2] [info] [mediation] Original Interstitial Ad waterfall
[Appodeal 3.0.2] [info] [mediation] Start to load Interstitial Ad admob precache wo pricefloor admob_interstitial_0.57 eCPM = 0.570000
```
# Activity Logs
Appodeal SDK provides API to log some mediation activity events.
Implement `APDActivityDelegate` protocol and set its instance to
Appodeal. These logs are not dependent on log level.
```swift showLineNumbers
@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
Appodeal.setActivityDelegate(self)
return true
}
}
extension AppDelegate: APDActivityDelegate {
func didReceive(_ activityLog: APDActivityLog) {
// TODO:
}
}
```
```objc showLineNumbers
@interace AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Appodeal setActivityDelegate:self];
return YES;
}
- (void)didReceiveActivityLog:(APDActivityLog *)activityLog {
// TODO:
}
@end
```
------------------
APDActivityLog contains information about activity type, ad type, ad
network and optional event-specific custom messages.
Available activity types are:
- `APDActivityTypeMediationStart` - mediation start for ad unit;
- `APDActivityTypeMediationFinish` - mediation finish for ad unit;
- `APDActivityTypeImpressionStart` - impression start for ad unit;
- `APDActivityTypeImpressionFinish` - impression finish for ad unit;
- `APDActivityTypeClick` - user interact with impression;
---
## Segments and Placements(Advanced)
## Segments
Segments are used to track statistics for various user categories and
manage ads for this categories. A segment is a fraction of audience
outlined based on certain parameters: e.g. gender, age or any other
parameters known to the app and passed to Appodeal SDK. Additional ad
management settings can be applied to each segment. Read more on
segments in our [FAQ](/advanced/segments).
Once user segments have been created, they can then be analyzed and used
to configure ads.
To create a new segment go [here](https://app.appodeal.com/v3/segments).
:::info
If you have no segments, all users will be assigned to default segment.
If you have multiple segments, their order is important. Only the first segment related to the given user will apply.
All of the rest will be ignored.
:::
-------------
### Manual Filters
Manual Filters allow to group users by any available metric. E.g.
you know the sources that directed users to your app and you want to
track the statistics for such sources — create a segment for each source
and mark each user with the source they came from.
To create such a segment, you have to set its name and value:
```swift showLineNumbers
Appodeal.setCustomStateValue(value: Any?, forKey: String)
```
```objc showLineNumbers
[Appodeal setCustomStateValue:(nullable id) forKey:(nonnull NSString *)]
```
------------------
Value can be boolean, numeric or string-based.
Example:
```swift showLineNumbers
Appodeal.setCustomStateValue(3, forKey: "levels_played")
```
```objc showLineNumbers
[Appodeal setCustomStateValue:@(3) forKey:@"levels_played"];
```
------------------
### Bought In-Apps and In-Apps Amount Filters
**Bought In-Apps** allows to group users by the fact of purchasing
in-apps. This will help you adjust the ads for such users or turn them
off if needed.
**In-Apps Amount** filter allows you to group users who've made a
particular amount of in-app purchases.
Please submit the purchase info via Appodeal SDK to make these settings
work correctly.
```swift showLineNumbers
Appodeal.track(inAppPurchase: 5, currency: "USD")
```
```objc showLineNumbers
[Appodeal trackInAppPurchase:@5 currency:@"USD"];
```
------------------
If you have no segments, all users will be assigned to default segment.
If you have multiple segments, their order is important. Only the first
segment related to the given user will apply. All of the rest will be
ignored.
## Placements
Appodeal SDK allows you to tag each impression with different placement. Read more on placements in our [FAQ](/advanced/placements).
To show an ad with placement, you have to call show method like this:
```swift showLineNumbers
Appodeal.showAd(.interstitial, forPlacement: "placement", rootViewController: controller)
```
```objc showLineNumbers
[Appodeal showAd:AppodealAdTypeInterstitial forPlacement:@"PLACEMENT" rootViewController:UIViewController];
```
------------------
To check if an impression is available for a given placement, use:
```swift showLineNumbers
Appodeal.canShow(.interstitial, forPlacement: "placement")
```
```objc showLineNumbers
[Appodeal canShow:AppodealAdTypeInterstitial forPlacement:@"PLACEMENT"];
```
------------------
You can configure your impression logic for each placement.
If you have no placements or call showAd with placement that does not
exist, the impression will be tagged with 'default' placement with
corresponding settings applied.
:::caution Important!
Placement settings affect ONLY ad presentation, not loading or caching.
:::
---
## Self-Hosted Bidon(Advanced)
Configuring and retrieving the Bidon endpoint.
:::info
Bidon documentation can be found [here](https://docs.bidon.org/).
:::
### Set Bidon Endpoint
To set a custom Bidon endpoint, use the following method:
```swift showLineNumbers
Appodeal.setBidonEndpoint("https://example.com/api")
```
```objc showLineNumbers
[Appodeal setBidonEndpoint:@"https://example.com/api"];
```
:::info Should be called before the SDK initialization.
:::
-------------
### Get Bidon Endpoint
To retrieve the currently set Bidon endpoint, use the following method:
```swift showLineNumbers
Appodeal.getBidonEndpoint()
```
```objc showLineNumbers
[Appodeal getBidonEndpoint];
```
-------------
---
## Testing(Advanced)
After adding a new app to Appodeal and integrating the SDK, we recommend testing your app.
Here are the tips for successful testing.
## Integration Review
### Step 1. Prepare Settings On Appodeal Side
#### Check Mediation Settings
Go to Application Settings → Mediation Settings → Line Items.
Choose the ad type you are interested in and check the network
connection.
In the Line Items section, you can see the rules for automatically
connecting ad networks. Once you fulfill all the requirements, networks
will be connected automatically using the default Appodeal account.
**Example:**
For new applications, a few networks will be connected by default if the Appodeal server receives a request for a
certain ad type.
If you see `This network will be activated by ad request.`
Try to [request real ads](#check-sdk-integration-with-real-ads) to activate this network using the default Appodeal
account.
:::info
Make sure you have **at least 2-3 enabled** networks. If the
requirements for automatic network connection are not fulfilled, link a
personal account using [Networks Setup](/networks-setup/introduction) to have more networks connected.
:::
Make sure the ad units are enabled for the connected networks:
#### Check Priorities (Waterfall Configuration).
Go to Application settings → Mediation Settings → Priorities, and
choose the ad type.
By default, only default priority configuration is enabled for the
waterfall, where all ad units from connected networks are placed. Make
sure line items have been added to your current configuration.
If not, add them to the configuration by dragging and dropping ad units
from the Unused Line Items list on the left to Automatic Priority.
### Step 2. Test Your SDK Integration
#### Check SDK Integration With Test Ads.
:::info
Test mode ads have a 100% fill rate, they load almost instantly compared
to real ads, which can take some time to load (0-30 seconds depending on
the ad type).
:::
1. [Enable Test Mode](#enable-test-mode)
2. [Enable SDK Logging](#enable-logging)
3. Make sure that all necessary adapters have been integrated into the
app. To get test ads, it's required to have all adapters marked by a
star in [Mediation Wizard](../get-started).
4. Run the app and go to all placements where you added ads. Make sure
they are loaded and shown successfully.
5. Open the logs tab and check Appodeal SDK logs. For more information,
look through the [SDK logging](logging)
:::info
Requests for test ads are not counted as real requests, however,
Appodeal needs at least one real request for automatically activating
networks for a certain ad type.
:::
#### Check SDK Integration With Real Ads.
We recommend testing apps using test mode to ensure proper performance
with real ads. However, it's necessary to make sure SDK integration is
correct and all networks are ready to use.
1. Disable test mode by commenting out the method you used to enable
it.
2. Check that Appodeal SDK [logging](#enable-logging) is enabled.
3. Make sure that all necessary adapters for the networks you are
planning to use have been integrated. For more information please
visit the [Mediation Wizard](../get-started).
4. Open your application and initialize SDK to make a request for
activating the ad networks. You can see all the activity of our SDK
in the logs under the "Appodeal" tag.
5. When network setup is ready, run the app again and open the logs
console. Make sure there are no errors in the logs. Use [SDK logging](logging)
to analyze Appodeal logs. Go through all placements where you added
ads. Make sure they are loaded and shown successfully with no
exceptions and errors
:::info
If your app is not published in one of the supported app stores (Google
Play, App Store, Amazon), the number of impressions for live ads is
restricted to [2,000](/faq-and-troubleshooting/faq/ad-mediation/traffic-limit).
:::
## Useful SDK Methods
### Enable Test Mode
Using test mode allows you to get our test ad creatives with 100%
fillrate.
```swift showLineNumbers
Appodeal.setTestingEnabled(true)
```
```objc showLineNumbers
[Appodeal setTestingEnabled: YES];
```
------------------
:::info
Should be called before the SDK initialization.
:::
### Enable Logging
To enable debug logging, use the code below:
```swift showLineNumbers
Appodeal.setLogLevel(.verbose)
```
```objc showLineNumbers
[Appodeal setLogLevel:APDLogLevelVerbose];
```
------------------
:::info
Should be called before the SDK initialization.
:::
Logs will be written in the console using the `"Appodeal"` tag.
Available parameters:
- `APDLogLeveloff `- logs off;
- `APDLogFlagError` - only error messages;
- `APDLogLevelWarning` - warning and error messages;
- `APDLogLevelDebug` - debug messages;
- `APDLogLevelInfo` - error, warning and information messages;
- `APDLogLevelVerbose` - all SDK and ad network messages.
### Disable Networks
```swift showLineNumbers
Appodeal.disableNetworks([ARRAY_OF_NETWORKS])
```
```objc showLineNumbers
[Appodeal disableNetworks:@[ARRAY_OF_NETWORKS]];
```
------------------
:::info
Should be called before the SDK initialization.
:::
Available parameters:
`adcolony`
`admob`
`amazon_ads`
`applovin`
`bidmachine`
`chartboost`
`facebook`
`ironsource`
`my_target`
`smaato`
`inmobi`
`mopub`
`ogury`
`startapp`
`tapjoy`
`unity_ads`
`vungle`
`yandex`
### Disable Networks For Specific Ad Types
```swift showLineNumbers
Appodeal.disableNetwork(for: .banner, name: "NETWORK_NAME")
Appodeal.disableNetwork(for: .nativeAd, name: "NETWORK_NAME")
Appodeal.disableNetwork(for: .MREC, name: "NETWORK_NAME")
Appodeal.disableNetwork(for: .interstitial, name: "NETWORK_NAME")
Appodeal.disableNetwork(for: .rewardedVideo, name: "NETWORK_NAME")
```
```objc showLineNumbers
[Appodeal disableNetworkForAdType:AppodealAdTypeBanner name:@"NETWORK_NAME"];
[Appodeal disableNetworkForAdType:AppodealAdTypeRewardedVideo name:@"NETWORK_NAME"];
[Appodeal disableNetworkForAdType:AppodealAdTypeInterstitial name:@"NETWORK_NAME"];
[Appodeal disableNetworkForAdType:AppodealAdTypeNativeAd name:@"NETWORK_NAME"];
[Appodeal disableNetworkForAdType:AppodealAdTypeMREC name:@"NETWORK_NAME"];
```
------------------
:::info Important
Should be called before the SDK initialization.
:::
---
## User Data(Advanced)
Our SDK provides user data tranfer for better ad targeting and higher eCPM. All parameters are optional.
## Set User Id
To assign an ID to a user, please call this method before Appodeal
initialization:
```swift showLineNumbers
Appodeal.setUserId("userId")
```
```objc showLineNumbers
[Appodeal setUserId:@"userId"];
```
------------------
:::caution
For data privacy and GDPR-compliance reasons, you may NOT use email address, phone number, real name or any other personally identifiable information in the user ID you set with this call.
:::
## Custom Segment Matching
If the logic of your application allows specifying user's
characteristics, then you can pass specific parameters to the Appodeal
SDK. You can
use [Segments](/advanced/segments) in the future.
- For gender use kAppodealUserGenderKey.
- For age use kAppodealUserAgeKey.
```swift showLineNumbers
Appodeal.setCustomStateValue("SOME_VALUE", forKey: "SOME_KEY")
// age
Appodeal.setCustomStateValue(AppodealUserGender.male.rawValue, forKey: kAppodealUserGenderKey)
// gender
Appodeal.setCustomStateValue(40, forKey: kAppodealUserAgeKey)
```
```objc showLineNumbers
[Appodeal setCustomStateValue:@"SOME_VALUE" forKey:@"SOME_KEY"];
// age
[Appodeal setCustomStateValue:AppodealUserGenderMale forKey: kAppodealUserGenderKey];
// gender
[Appodeal setCustomStateValue:40 forKey: kAppodealUserAgeKey];
```
------------------
## Location
The Appodeal SDK reads the device location only if your app has already obtained
the OS location permission from the user. The SDK **does not request the location
permission itself** — your app is responsible for requesting authorization via
Core Location (and providing the `NSLocationWhenInUseUsageDescription` Info.plist
entry).
**To opt out of location collection, do not request the location permission** (and
omit the `NSLocationWhenInUseUsageDescription` key). If the permission is missing,
no location is collected.
With `NSLocationWhenInUse`/`Always` granted, the SDK may collect precise or
approximate location depending on the accuracy the user allowed. Declare this
in your [App Privacy Details on the App Store](/ios/data-protection/app-privacy-details).
## Send Extra Data
You can send key-value data to Appodeal.
```swift showLineNumbers
Appodeal.setExtrasValue("SOME_VALUE", forKey: "SOME_KEY")
```
```objc showLineNumbers
[Appodeal setExtrasValue:@"SOME_VALUE" forKey:@"SOME_KEY"];
```
------------------
To send the device identifier from a mobile attribution service and
match it with the Appodeal user id, use "attribution_id" as a key and a
unique identifier from your attribution service as a value and if you
use this method for attribution, call it **before Appodeal SDK
initialization.**
---
## Get Started(Unity)
| Release Version | Release Date |
| --- | --- |
| { getReleaseVersion("unity") } | { getReleaseDate("unity") } |
Follow this guide to get the best out of Appodeal.
The Appodeal SDK gives you **access to 70+ Ad Demand Sources and makes them compete against each other in a real-time
auction**, maximizing your ad revenues. The Appodeal SDK also provides *In-app Bidding, Automatic UA Optimization,
User Segmentation & A/B Testing, Cross-Promotion and Direct Deals, Instant Payouts*,
and [much](https://appodeal.com/monetization/) [more](/faq-and-troubleshooting/faq/ad-mediation/getting-started-with-ad-mediation).
:::info Integration Options
Appodeal SDK provides **two** ways of integration. From the options below, choose the one that fits your needs better.
If you plan to run UA campaigns, want to analyze your metrics in our Appodeal's business intelligence tool
without using MMP, or want to use remote config for tests and settings, your option is - **the full package**.
:::
**The Appodeal SDK Full Package** - The Appodeal SDK provides you with tools to grow your mobile apps and games.
In addition to the monetization services, you can benefit from UA (User Acquisition) and in-app analytics services.
Here is the list of services Appodeal SDK Full Package includes:
- [Get started with Appodeal](get-started) to gain access to **Monetization** and **Analytics**.
- Connect with [Adjust](./services/adjust) or [AppsFlyer](./services/appsflyer) to unlock **Attribution features**.
- Connect with [Meta](./services/meta) (*formerly known as facebook-core)* for **User Acquisition**.
- Connect with [Firebase](./services/firebase) for **Analytics** + remote config for **product A/B tests** and settings.
If you plan to run UA campaigns, want to analyze your metrics in our Appodeal's business intelligence tool without
using MMP, or want to use remote config for tests and settings, your option is - **The Appodeal SDK Full Package**.
**The Appodeal SDK Mediation only** - If you do not plan to run (*UA*) User Acquisition campaigns, nor want to use
Appodeal advanced analytics, we have created a lite version of our SDK, only with mediation. During the integration,
you will not be required to install any additional services apart from mediation. This may speed up your integration
process, and you can always upgrade to the Full Package whenever you're ready.
:::tip
Please follow this integration guide step by step and choose your integration option when needed.
:::
Also, remember that you can always customize Appodeal SDK with the help of
our [Appodeal Plugin Manager](./advanced/appodeal-plugin-manager) guide.
-------------
The following document shows how to integrate Appodeal in your Unity project with your desired networks,
and configure all your ad formats.
:::info Minimum Requirements:
- Unity 2021.3.0+, 2022.3.0+, 6000.0.23+
- Android API level 24 (Android OS 7.0) and above
- iOS 13.0 or higher (only if you use Firebase or LevelPlay, iOS 12.4 if you use MyTarget, otherwise iOS 12.0 is sufficient)
- Xcode 16.4 or higher
- CocoaPods 1.12.0 or higher
- [Git](https://git-scm.com) must be installed on your device
:::
:::info Minimum Requirements:
- Unity 2017.4 (Api Compatibility Level - Experimental (.NET 4.6 Equivalent) or 2018.3+
- Android API level 24 (Android OS 7.0) and above
- iOS 13.0 or higher
- Xcode 16.4 or higher
- CocoaPods 1.12.0 or higher
:::
:::info Unity 2017.4
If you are using Unity 2017.4, you need to change Scripting Runtime Version in *Player Settings > Other Setting*
to Experimental (.NET 4.6 Equivalent).
:::
You can use our demo app as a reference project.
Check our video guide on how to integrate Appodeal SDK in your app.
-------------
## Step 1. Import SDK
Choose the desired integration type and follow the steps below to add Appodeal Plugin into your project.
1. Download External Dependency Manager (**.tgz** one) v1.2.175 or newer from this
[website](https://developers.google.com/unity/archive#external_dependency_manager_for_unity).
2. Import EDM into your Unity project by adding the downloaded archive via Unity Package Manager
(Window → Package Manager → "+" → Add package from **tarball**).
2. Copy the link below, head to the *Window → Package Manager → "+" → Add package from git URL*, paste
the copied link there and press enter.
{`https://github.com/appodeal/appodeal-unity-plugin-upm.git#v${getReleaseVersion("unity")}`}
1. Download Appodeal Unity Plugin, which includes the newest Android and iOS Appodeal SDKs
with major improvements, by clicking on the button below.
2. To import Appodeal Unity Plugin, double-click on the Appodeal-Unity-Plugin-{getReleaseVersion("unity")}-{getBuildDate("unity")}.unitypackage file,
or go to the *Assets → Import Package → Custom Package*. Keep all the files in the **Importing Package**
window selected, and click **Import**.
:::tip Version Management
Use Appodeal Plugin Manager to update to the latest Appodeal SDK from the Unity menu bar
(*Appodeal → Plugin Configuration*), which supports Unity 2018.3 or higher.
You can find more information in [our blog](https://appodeal.com/blog/unity-plugin-sdk-manager).
:::
-------------
## Step 2. Configure Project
In case you would rather use the Appodeal SDK Full Package along with its services, please ensure you have **not
excluded** them in your project (*Appodeal → Plugin Configuration*).
Please read our [Appodeal Plugin Manager](./advanced/appodeal-plugin-manager) guide for more detailed information.
In case you are using Appodeal SDK Mediation Only, ensure you have **excluded** the "full package services"
in your project (*Appodeal → Plugin Configuration*).
Please read our [Appodeal Plugin Manager](./advanced/appodeal-plugin-manager) guide to learn how to exclude services.
### Android Configuration
#### Gradle Settings
:::caution Preparing your Gradle build for Android 11
Android 11 changes how apps can query and interact with other apps that the user has installed on a device.
For that reason make sure you're using Gradle version that matches
one of listed [here](https://developer.android.com/build/releases/gradle-plugin#4-0-0).
:::
:::warning Android min api version < 26
Apps with `minApiVersion` below 26 may encounter compatibility issues with GoogleAds Identifier 18.2.0.
**Solution:** Enable core library desugaring in your build.gradle. See our [troubleshooting guide](/faq-and-troubleshooting/troubleshooting/unity-common-issues/desugaring) for step-by-step instructions.
:::
1. Go to *Preferences → External Tools* and update your gradle version to 6.7.1.
2. Go to *Player Settings → Publishing Settings* and enable **Custom Base Gradle Template** flag.
3. Go to *Assets → Plugins → Android → baseProjectTemplate.gradle*, open the file and change
`classpath 'com.android.tools.build:gradle:3.4.0'` to `classpath 'com.android.tools.build:gradle:4.2.0'`.
1. Go to *Preferences → External Tools* and update your gradle version to 6.7.1.
2. Go to *Player Settings → Publishing Settings* and enable **Custom Base Gradle Template** flag.
3. Go to *Assets → Plugins → Android → baseProjectTemplate.gradle*, open the file and change
`classpath 'com.android.tools.build:gradle:3.6.0'` to `classpath 'com.android.tools.build:gradle:4.2.0'`.
No additional steps required.
:::info If you have EDM Plugin v1.2.175 or older, please check this [guide](/faq-and-troubleshooting/troubleshooting/unity-common-issues/could-not-find-comappodealadssdk).
:::
1. Go to *Preferences → External Tools* and update your gradle version to 7.6.
2. Go to *Player Settings → Publishing Settings* and enable **Custom Base Gradle Template** flag.
3. Go to *Assets → Plugins → Android → baseProjectTemplate.gradle*, open the file and change
`'com.android.application' version '7.4.2', 'com.android.library' version '7.4.2'` to
`'com.android.application' version '7.3.1', 'com.android.library' version '7.3.1'`.
:::info If you have EDM Plugin v1.2.175 or older, please check this [guide](/faq-and-troubleshooting/troubleshooting/unity-common-issues/could-not-find-comappodealadssdk).
:::
1. Go to *Preferences → External Tools* and update your gradle version to 8.4.
2. Go to *Player Settings → Publishing Settings* and enable **Custom Base Gradle Template** flag.
3. Go to *Assets → Plugins → Android → baseProjectTemplate.gradle*, open the file and change
`'com.android.application' version '8.3.0', 'com.android.library' version '8.3.0'` to
`'com.android.application' version '8.2.2', 'com.android.library' version '8.2.2'`.
:::info If you have EDM Plugin v1.2.175 or older, please check this [guide](/faq-and-troubleshooting/troubleshooting/unity-common-issues/could-not-find-comappodealadssdk).
:::
No additional steps required.
:::info If you have EDM Plugin v1.2.175 or older, please check this [guide](/faq-and-troubleshooting/troubleshooting/unity-common-issues/could-not-find-comappodealadssdk).
:::
-------------
#### External Dependency Manager
Appodeal Unity Plugin uses External Dependency Manager package.
You need to complete the following steps to resolve Appodeal's dependencies:
1. Before switching to Android platform, select *File → Build Settings → Android* in Unity menu bar.
2. Add flag Custom Gradle Template for Unity 2017.4 - Unity 2019.2 versions or for Unity 2019.3 and higher
activate the following toggles under *Build Settings → Player Settings → Publishing settings*:
- Custom Main Gradle Template
- Custom Gradle Properties Template
- Custom Gradle Settings Template
3. Enable the **Patch mainTemplate.gradle** option (*Assets → External Dependency Manager → Android Resolver → Settings*).
4. Enable the **Copy and patch settingsTemplate.gradle from 2022.2** option (*Assets → External Dependency Manager → Android Resolver → Settings*).
5. Enable the **Use Jetifier** option (*Assets → External Dependency Manager → Android Resolver → Settings*).
6. Then run *Assets → External Dependency Manager → Android Resolver* and press **Resolve** or **Force Resolve**.
As a result, the modules, that are required for Appodeal SDK work, will be imported to project's mainTemplate.gradle file.
-------------
#### Configure AndroidManifest.xml
:::caution Prevent Overwriting
Before making changes to the AndroidManifest.xml file, make sure to remove the first line which is:
```xml
```
:::
:::info Permissions Overview
We distinguish 2 sets of permissions: required permissions, that are obligatory for the correct work of Appodeal SDK,
and optional permissions, that can be used for better targeting. For more information about the purpose
of each of the permissions, see this [FAQ](/faq-and-troubleshooting/troubleshooting/general/android-sdk-permissions) section.
- These are the required permissions
```xml
```
- These are the optional permissions
```xml
```
:::
Required permissions are already added to the project by Appodeal Unity Plugin.
If you want to use any of the optional permissions, add them by following one of the methods listed below.
Go to *Plugins → Android* folder, open **AndroidManifest.xml** file and add there permissions you want.
Open *Appodeal → Appodeal Settings* window in Unity top bar menu. Tick all the optional permissions you want.
-------------
Some networks and 3rd party dependencies (related to network dependencies) can include their own permissions to the manifest.
If you want to force remove such permissions you can refer to this
[guide](https://developer.android.com/studio/build/manifest-merge#node_markers).
:::danger Location Permission Usage Features
According to [Google policy](https://support.google.com/googleplay/android-developer/answer/9857753?hl=en),
location permissions may only be requested to provide features beneficial to the user and relevant to the core
functionality of the app. You cannot request access to location data for the sole purpose of advertising or analytics.
**If you are not using location for the main functions of your app**
1. Remove location permission in your app by adding the following code under the `` tag in **AndroidManifest.xml** file located at the
*Assets/Plugins/Android* directory.
```xml
```
2. Update the app on Google Play. During the publishing process, make sure there are no location warnings in Google Play Console.
**If you are using location for the main functions of your app**
1. Fill out the Location permissions declaration form in
[Google Play Console](https://play.google.com/console/u/0/developers/app/app-content/permission-declarations).
You can read more about the declaration form
[here](https://support.google.com/googleplay/android-developer/answer/9799150?hl=en#zippy=%2Cwhere-do-i-find-the-declaration).
2. Update the app on Google Play. During the publishing process, make sure there are no location warnings in Google Play Console.
:::
-------------
#### Multidex Support
- If you are using Unity v2019.2 and versions below you need to add Multidex support to your project.
Follow [this guide](https://developer.android.com/studio/build/multidex.html) to add Multidex.
- If you are using Unity v2019.3 or higher go to *Player Settings → Publishing Settings → Other Settings*
and change Minimum API Level to 21 or higher.
:::note Google Play Distribution Reqs
Make sure to add Privacy Policy to your app on Google Play that links to
[Appodeal's Privacy Policy](https://www.appodeal.com/privacy-policy) to avoid violating
[Google Play Developer Distribution Agreement](https://play.google.com/about/developer-distribution-agreement.html).
:::
-------------
### iOS Configuration
#### External Dependency Manager
- Turn off **Link frameworks statically** option in EDM
(*Assets → External Dependency Manager → iOS Resolver → Settings → Link frameworks statically*).
#### Add SKAdNetworkIds
:::important
To ensure SKAdNetwork packages can be correctly scanned, follow the [Semantic Versioning (SemVer) format](../advanced/app-version-format) for your app version.
:::
Ad networks used in Appodeal mediation support conversion tracking using Apple's
[SKAdNetwork](https://developer.apple.com/documentation/storekit/skadnetwork), which means ad networks are able
to attribute an app install even when IDFA is unavailable. To enable this functionality, you will need to update
the `SKAdNetworkItems` key with an additional dictionary in your
[Info.plist](https://developer.apple.com/documentation/storekit/skadnetwork/configuring_a_source_app).
1. Select **Info.plist** in the Project navigator in Xcode
2. Right-click on **Info.plist** file → Open as → Source Code
3. Copy the **SKAdNetworkItems** from below and paste it into your **Info.plist** file
There is SKAdNetworks IDs in Info.plist format
Open *Appodeal → Appodeal Settings* window in Unity top bar menu. Tick the corresponding checkbox.
-------------
#### Configure App Transport Security Settings
In order to serve ads, the SDK requires you to allow arbitrary loads. Set up NSAppTransportSecurity key
to allow arbitrary loads following the steps:
1. Go to your **info.plist** file, then press `Add+` anywhere in the first column of the key list.
2. Add `App Transport Security Setting key` and set its type to `Dictionary` in the second column.
3. Press `Add+` at the end of the name `App Transport Security Settings key` and choose `Allow Arbitrary loads` option.
Set its type to `Boolean` and its value to `Yes`.
You can also add the key to your **Info.plist** directly, using this code:
```xml
NSAppTransportSecurityNSAllowsArbitraryLoads
```
Open *Appodeal → Appodeal Settings* window in Unity top bar menu. Tick the corresponding checkbox.
-------------
#### Other Feature Usage Descriptions
To improve ad performance the the following entries may be added:
1. **NSUserTrackingUsageDescription** - As of iOS 14 using IDFA requires permission from the user.
The following entry must be added in order to improve ad performance.
2. **NSLocationWhenInUseUsageDescription** - Entry is required if your application allows Appodeal SDK to use location data.
3. **NSCalendarsUsageDescription** - Recommended by some ad networks.
Add any of the keys to your **Info.plist** file, using this template:
```xml
NSUserTrackingUsageDescription$(APP_NAME) needs your advertising identifier to provide personalised advertising experience tailored to youNSLocationWhenInUseUsageDescription$(APP_NAME) needs your location for analytics and advertising purposesNSCalendarsUsageDescription$(APP_NAME) needs your calendar to provide personalised advertising experience tailored to you
```
Open *Appodeal → Appodeal Settings* window in Unity top bar menu. Tick the corresponding checkbox.
-------------
#### Known Issues
##### 1. Empty CFBundleVersion Issue
As a result of this [error](https://developer.apple.com/forums/thread/734170) found in Xcode 14 and persisting in Xcode 15,
the values for `Build` and `Version` may appear blank within your `.xcworkspace` file under
the navigation path *Target → General → Identity*.
If you are not able to upload your app to the App Store due to
`Connect Operation Error CFBundleShortVersionString is empty but must be composed of one to three period-separated integers`
error, or do not get ads in your app after completing the above integration steps of Appodeal SDK make sure that you have the
package version in your generated Xcode project.
Navigate to the `.xcworkspace` file → Target → General → Identity → and add your app version if it is empty.
##### 2. AdColony Presentation Issue
AdColony always checks, if the key window’s `rootViewController` matches the passed `rootViewController`.
Otherwise, Adcolony fails to present the ad. If your app has multiple independent windows,
you can get a message with this or similar text:
:::info AdColony Error Message
AdColony [*** ERROR ***] : AdColony has ads, but could not display them. AdColony was unable to find
the currently visible `UIViewController` for your app. Please ensure that your key `UIWindow` has a `rootViewController`.
:::
It means, that the `rootViewController` that was used in `showAd`, doesn't belong to the first window in the array.
### AdMob Configuration
:::caution Following this step is necessary only if you have AdMob adapter connected.
:::
:::warning Important
Admob Bidding is now available with **Appodeal SDK 3.2.0**.
Don't forget to download our newest version of Admob Sync tool from this [page](https://amsa-updates.appodeal.com/) and perform sync.
You can read more about Admob Sync in our [guide](/networks-setup/ad-networks/network-connection/admob-sync).
:::
AdMob App ID is the unique ID assigned to your app.
To find the AdMob App ID in your AdMob account, go to *Apps → your application → app settings* and copy the AdMob App ID.
Add AdMob App Ids from the Unity Menu bar *Appodeal → Appodeal Settings* tool for each platform.
For more information about Admob sync check out our Admob connection [guide](/networks-setup/ad-networks/network-connection/admob).
-------------
## Step 3. Initialize SDK
Before loading and displaying ads, you need to initialize Appodeal SDK, as follows:
1. Import Namespaces
```csharp showLineNumbers
using AppodealStack.Monetization.Api;
using AppodealStack.Monetization.Common;
```
```csharp showLineNumbers
using AppodealAds.Unity.Api;
using AppodealAds.Unity.Common;
```
2. Call Initialization Method
Add the following code snippet to `Start()` (or whatever you want) method of your main scene’s MonoBehaviour
```csharp showLineNumbers
class Test : MonoBehaviour
{
private void Start()
{
int adTypes = AppodealAdType.Interstitial | AppodealAdType.Banner | AppodealAdType.RewardedVideo | AppodealAdType.Mrec;
string appKey = "YOUR_APPODEAL_APP_KEY";
AppodealCallbacks.Sdk.OnInitialized += OnInitializationFinished;
Appodeal.Initialize(appKey, adTypes);
}
public void OnInitializationFinished(object sender, SdkInitializedEventArgs e) {}
}
```
:::caution Make sure to replace `YOUR_APPODEAL_APP_KEY` with the actual app key.
:::
Use the type codes below to set the preferred ad format:
- `AppodealAdType.Interstitial` for interstitial.
- `AppodealAdType.RewardedVideo` for rewarded videos.
- `AppodealAdType.Banner` for banners.
- `AppodealAdType.Mrec` for 300*250 banners.
:::tip Useful Tip:
Ad types can be combined using `|` operator. For example, `AppodealAdType.Interstitial | AppodealAdType.RewardedVideo`.
Initialize only those ad types you want to use in your app to avoid getting ad requests to unused ones.
:::
```csharp showLineNumbers
class Test : MonoBehaviour, IAppodealInitializationListener
{
private void Start()
{
int adTypes = Appodeal.INTERSTITIAL | Appodeal.BANNER | Appodeal.REWARDED_VIDEO | Appodeal.MREC;
string appKey = "YOUR_APPODEAL_APP_KEY";
Appodeal.initialize(appKey, adTypes, this);
}
public void onInitializationFinished(List errors) {}
}
```
:::caution Make sure to replace `YOUR_APPODEAL_APP_KEY` with the actual app key.
:::
Use the type codes below to set the preferred ad format:
- `Appodeal.INTERSTITIAL` for interstitial.
- `Appodeal.REWARDED_VIDEO` for rewarded videos.
- `Appodeal.BANNER` for banners.
- `Appodeal.MREC` for 300*250 banners.
:::tip Useful Tip:
Ad types can be combined using `|` operator. For example, `Appodeal.INTERSTITIAL | Appodeal.REWARDED_VIDEO`.
Initialize only those ad types you want to use in your app to avoid getting ad requests to unused ones.
:::
-------------
## Step 4. Configure Ad Types
Appodeal SDK is now imported and you're ready to implement an ad. Appodeal offers a number of different ad formats,
so you can choose the one that best fits your app's user experience.
-------------
## Step 5. What's next
### Add App-ads.txt File
The app-ads.txt file is a text file which provides a mechanism for publishers to declare their authorized digital sellers.
You can find detailed information [here](../../advanced/app-ads)
---
## Banner(3)
Banner ads are classic static banners, which are usually located at the bottom or top of the screen.
Appodeal supports traditional 320x50 banners, tablet 728x90 ones, and adaptive banners (for Admob only)
that adjust to the size and orientation of the device.
:::note You can display only one banner view on the screen.
:::
You can use our **demo app** as a reference project.
-------------
## Fixed Positioned Banner
### Display
Banner ads are refreshed every 15 seconds automatically by default. To display banner, you need to call
one of the following methods:
```csharp
// Display banner at the bottom of the screen
Appodeal.Show(AppodealShowStyle.BannerBottom);
// Display banner at the top of the screen
Appodeal.Show(AppodealShowStyle.BannerTop);
// Display banner at the left of the screen
Appodeal.Show(AppodealShowStyle.BannerLeft);
// Display banner at the right of the screen
Appodeal.Show(AppodealShowStyle.BannerRight);
```
```csharp
// Display banner at the bottom of the screen
Appodeal.show(Appodeal.BANNER_BOTTOM);
// Display banner at the top of the screen
Appodeal.show(Appodeal.BANNER_TOP);
// Display banner at the left of the screen
Appodeal.show(Appodeal.BANNER_LEFT);
// Display banner at the right of the screen
Appodeal.show(Appodeal.BANNER_RIGHT);
```
-------------
### Hide Banner
To hide a banner that was shown via `Appodeal.Show()` method use the following method:
```csharp
Appodeal.Hide(AppodealAdType.Banner);
```
```csharp
Appodeal.hide(Appodeal.BANNER);
```
-------------
### Check If Ad Is Loaded
You can check whether or not an ad is loaded at a certain moment. This method returns a boolean value,
representing the banner ad loading status.
```csharp
Appodeal.IsLoaded(AppodealAdType.Banner);
```
```csharp
Appodeal.isLoaded(Appodeal.BANNER);
```
:::note
The `Appodeal.Show()` method for banners can be called at any moment. If there is no ad available,
we will cache one and show it right away.
:::
-------------
### Callbacks
The callbacks are used to track different events in the lifecycle of an ad, e.g. when an ad was clicked on or closed.
Follow the steps below to implement them:
Subscribe to the desired banner event using one of the options from this
[guide](../advanced/sdk-events). (you can subscribe to any number of events you want)
```csharp
AppodealCallbacks.Banner.OnLoaded += (sender, args) => { };
```
You will find all existing banner events in the example below:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.Banner.OnLoaded += OnBannerLoaded;
AppodealCallbacks.Banner.OnFailedToLoad += OnBannerFailedToLoad;
AppodealCallbacks.Banner.OnShown += OnBannerShown;
AppodealCallbacks.Banner.OnShowFailed += OnBannerShowFailed;
AppodealCallbacks.Banner.OnClicked += OnBannerClicked;
AppodealCallbacks.Banner.OnExpired += OnBannerExpired;
}
#region BannerAd Callbacks
// Called when a banner is loaded (height arg shows banner's height, precache arg shows if the loaded ad is precache
private void OnBannerLoaded(object sender, BannerLoadedEventArgs e)
{
Debug.Log("Banner loaded");
}
// Called when banner failed to load
private void OnBannerFailedToLoad(object sender, EventArgs e)
{
Debug.Log("Banner failed to load");
}
// Called when banner failed to show
private void OnBannerShowFailed(object sender, EventArgs e)
{
Debug.Log("Banner show failed");
}
// Called when banner is shown
private void OnBannerShown(object sender, EventArgs e)
{
Debug.Log("Banner shown");
}
// Called when banner is clicked
private void OnBannerClicked(object sender, EventArgs e)
{
Debug.Log("Banner clicked");
}
// Called when banner is expired and can not be shown
private void OnBannerExpired(object sender, EventArgs e)
{
Debug.Log("Banner expired");
}
#endregion
```
1. Extend your class with `IBannerAdListener` interface:
```csharp
class SomeClassName : IBannerAdListener {}
```
2. Implement all required callback methods:
```csharp showLineNumbers
#region Banner callback handlers
// Called when a banner is loaded (height arg shows banner's height, precache arg shows if the loaded ad is precache
public void onBannerLoaded(int height, bool precache)
{
Debug.Log("Banner loaded");
}
// Called when banner failed to load
public void onBannerFailedToLoad()
{
Debug.Log("Banner failed to load");
}
// Called when banner is shown
public void onBannerShown()
{
Debug.Log("Banner shown");
}
// Called when banner failed to show
public void onBannerShowFailed()
{
Debug.Log("Banner show failed");
}
// Called when banner is clicked
public void onBannerClicked()
{
Debug.Log("Banner clicked");
}
// Called when banner is expired and can not be shown
public void onBannerExpired()
{
Debug.Log("Banner expired");
}
#endregion
```
3. Call the following method:
```csharp
Appodeal.setBannerCallbacks(this);
```
:::note Unity Main Thread
All callbacks are called on native main threads that do not match the main thread of the Unity. If you need to
receive callbacks in the main Unity thread follow our [Callback Usage Guide](../advanced/main-thread-callbacks).
:::
-------------
## Custom Positioned Banner
### Displaying Banner At Custom Position
Banner ad can be moved along the axis to the desired position.
To show Banner at Custom Position use the following method:
```csharp
Appodeal.ShowBannerView(yPosition, xPosition, "placementName");
```
Use int value or one of the constants below for `yPosition`:
- `AppodealViewPosition.VerticalTop` — to align a banner to the top of the screen.
- `AppodealViewPosition.VerticalBottom` — to align a banner to the bottom of the screen.
Use int value or one of the constants below for `xPosition`:
- `AppodealViewPosition.HorizontalSmart` — to use the full-screen width.
- `AppodealViewPosition.HorizontalCenter` — to center a banner horizontally.
- `AppodealViewPosition.HorizontalRight` — to align a banner to the right.
- `AppodealViewPosition.HorizontalLeft` — to align a banner to the left.
Banner ad can be moved along the axis to the desired position.
To show Banner at Custom Position use the following method:
```csharp
Appodeal.showBannerView(yPosition, xPosition, "placementName");
```
Use int value or one of the constants below for `yPosition`:
- `Appodeal.BANNER_TOP` — to align a banner to the top of the screen.
- `Appodeal.BANNER_BOTTOM` — to align a banner to the bottom of the screen.
Use int value or one of the constants below for `xPosition`:
- `Appodeal.BANNER_HORIZONTAL_SMART` — to use the full-screen width.
- `Appodeal.BANNER_HORIZONTAL_CENTER` — to center a banner horizontally.
- `Appodeal.BANNER_HORIZONTAL_RIGHT` — to align a banner to the right.
- `Appodeal.BANNER_HORIZONTAL_LEFT` — to align a banner to the left.
:::info Banner positioning is relative to the top left corner of the screen.
:::
-------------
### Hide Banner
To hide a banner that was shown via `Appodeal.ShowBannerView()` method use the following method:
```csharp
Appodeal.HideBannerView();
```
```csharp
Appodeal.hideBannerView();
```
-------------
## Advanced
### Placements
Appodeal SDK allows you to tag each impression with different placement. To use placements, you need to
create placements in Appodeal Dashboard.
[Read more](/advanced/placements) about placements.
To show an ad with placement, you have to call show method with specifying placement's name:
```csharp
Appodeal.Show(AppodealShowStyle.BannerTop, "placementName");
```
```csharp
Appodeal.show(Appodeal.BANNER_TOP, "placementName");
```
-------------
### Destroy Hidden Banner
To free memory from hidden banner call the code below:
```csharp
Appodeal.Destroy(AppodealAdType.Banner);
```
```csharp
Appodeal.destroy(Appodeal.BANNER);
```
-------------
### Get Predicted eCPM
This method returns expected eCPM for a currently cached advertisement. The amount is calculated based on
historical data for the current ad unit.
```csharp
Appodeal.GetPredictedEcpm(AppodealAdType.Banner);
```
```csharp
Appodeal.getPredictedEcpm(Appodeal.BANNER);
```
-------------
### Enable 728x90 Banners
To enable 728*90 banner use the following method before initialization:
```csharp
Appodeal.SetTabletBanners(true);
```
```csharp
Appodeal.setTabletBanners(true);
```
-------------
### Disable Banner Refresh Animation
To disable banner refresh animation use the following method before initialization:
```csharp
Appodeal.SetBannerAnimation(false);
```
```csharp
Appodeal.setBannerAnimation(false);
```
-------------
### Smart Banners
Smart banners are the banner ads which automatically fit the screen size. Using them helps to deal with the
increasing fragmentation of the screen sizes on different devices. In the Appodeal SDK the smart banners are
enabled by default. To disable them, use the following method before initialization:
```csharp
Appodeal.SetSmartBanners(false);
```
```csharp
Appodeal.setSmartBanners(false);
```
-------------
---
## Interstitial(3)
Interstitial ads are full-screen advertisements. In Appodeal, they are divided into two types - static interstitial
and video interstitial.
These types of ads are requested simultaneously during caching. If both of them filled an ad, the most expensive
of the two will be shown.
**Static interstitial** - static full-screen banners.
**Video interstitial** - these are videos that the user can usually close 5 seconds after the start of viewing.
You can use our **demo app** as a reference project.
-------------
## Check If Ad Is Loaded
You can check whether or not an ad is loaded at a certain moment. This method returns a boolean value,
representing the interstitial ad loading status.
```csharp
Appodeal.IsLoaded(AppodealAdType.Interstitial);
```
```csharp
Appodeal.isLoaded(Appodeal.INTERSTITIAL);
```
:::tip Check Ad Loading Status
We recommend you always check whether an ad is available before trying to show it.
*Example*:
```csharp showLineNumbers
if(Appodeal.IsLoaded(AppodealAdType.Interstitial)) {
Appodeal.Show(AppodealShowStyle.Interstitial);
}
```
```csharp showLineNumbers
if(Appodeal.isLoaded(Appodeal.INTERSTITIAL)) {
Appodeal.show(Appodeal.INTERSTITIAL);
}
```
:::
-------------
## Display
To show an interstitial ad, you need to call the following method:
```csharp
Appodeal.Show(AppodealShowStyle.Interstitial);
```
```csharp
Appodeal.show(Appodeal.INTERSTITIAL);
```
-------------
## Manual Caching
If you need more control of interstitial ads loading use manual caching. Manual caching for Interstitial
can be useful to [improve display rate](https://blog.appodeal.com/whats-display-rate/) or decrease
SDK loads when several ad types are cached.
1. To disable automatic caching for interstitials, use the code below before the SDK initialization:
```csharp
Appodeal.SetAutoCache(AppodealAdType.Interstitial, false);
```
```csharp
Appodeal.setAutoCache(Appodeal.INTERSTITIAL, false);
```
2. To cache Interstitial ad manually use the following method:
```csharp
Appodeal.Cache(AppodealAdType.Interstitial);
```
```csharp
Appodeal.cache(Appodeal.INTERSTITIAL);
```
-------------
## Callbacks
The callbacks are used to track different events in the lifecycle of an ad, e.g. when an ad was clicked on or closed.
Follow the steps below to implement them:
Subscribe to the desired interstitial event using one of the options from this
[guide](../advanced/sdk-events). (you can subscribe to any number of events you want)
```csharp
AppodealCallbacks.Interstitial.OnLoaded += (sender, args) => { };
```
You will find all existing interstitial events in the example below:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.Interstitial.OnLoaded += OnInterstitialLoaded;
AppodealCallbacks.Interstitial.OnFailedToLoad += OnInterstitialFailedToLoad;
AppodealCallbacks.Interstitial.OnShown += OnInterstitialShown;
AppodealCallbacks.Interstitial.OnShowFailed += OnInterstitialShowFailed;
AppodealCallbacks.Interstitial.OnClosed += OnInterstitialClosed;
AppodealCallbacks.Interstitial.OnClicked += OnInterstitialClicked;
AppodealCallbacks.Interstitial.OnExpired += OnInterstitialExpired;
}
#region InterstitialAd Callbacks
// Called when interstitial was loaded (precache flag shows if the loaded ad is precache)
private void OnInterstitialLoaded(object sender, AdLoadedEventArgs e)
{
Debug.Log("Interstitial loaded");
}
// Called when interstitial failed to load
private void OnInterstitialFailedToLoad(object sender, EventArgs e)
{
Debug.Log("Interstitial failed to load");
}
// Called when interstitial was loaded, but cannot be shown (internal network errors, placement settings, etc.)
private void OnInterstitialShowFailed(object sender, EventArgs e)
{
Debug.Log("Interstitial show failed");
}
// Called when interstitial is shown
private void OnInterstitialShown(object sender, EventArgs e)
{
Debug.Log("Interstitial shown");
}
// Called when interstitial is closed
private void OnInterstitialClosed(object sender, EventArgs e)
{
Debug.Log("Interstitial closed");
}
// Called when interstitial is clicked
private void OnInterstitialClicked(object sender, EventArgs e)
{
Debug.Log("Interstitial clicked");
}
// Called when interstitial is expired and can not be shown
private void OnInterstitialExpired(object sender, EventArgs e)
{
Debug.Log("Interstitial expired");
}
#endregion
```
1. Extend your class with `IInterstitialAdListener` interface:
```csharp
class SomeClassName : IInterstitialAdListener {}
```
2. Implement all required callback methods:
```csharp showLineNumbers
#region Interstitial callback handlers
// Called when interstitial was loaded (precache flag shows if the loaded ad is precache)
public void onInterstitialLoaded(bool isPrecache)
{
Debug.Log("Interstitial loaded");
}
// Called when interstitial failed to load
public void onInterstitialFailedToLoad()
{
Debug.Log("Interstitial failed to load");
}
// Called when interstitial was loaded, but cannot be shown (internal network errors, placement settings, etc.)
public void onInterstitialShowFailed()
{
Debug.Log("Interstitial show failed");
}
// Called when interstitial is shown
public void onInterstitialShown()
{
Debug.Log("Interstitial shown");
}
// Called when interstitial is closed
public void onInterstitialClosed()
{
Debug.Log("Interstitial closed");
}
// Called when interstitial is clicked
public void onInterstitialClicked()
{
Debug.Log("Interstitial clicked");
}
// Called when interstitial is expired and can not be shown
public void onInterstitialExpired()
{
Debug.Log("Interstitial expired");
}
#endregion
```
3. Call the following method:
```csharp
Appodeal.setInterstitialCallbacks(this);
```
:::note Unity Main Thread
All callbacks are called on native main threads that do not match the main thread of the Unity. If you need to
receive callbacks in the main Unity thread follow our [Callback Usage Guide](../advanced/main-thread-callbacks).
:::
-------------
## Placements
Appodeal SDK allows you to tag each impression with different placement. To use placements, you need to
create placements in Appodeal Dashboard.
[Read more](/advanced/placements) about placements.
To show an ad with placement, you have to call show method with specifying placement's name:
```csharp
Appodeal.Show(AppodealShowStyle.Interstitial, "placementName");
```
```csharp
Appodeal.show(Appodeal.INTERSTITIAL, "placementName");
```
If the loaded ad can’t be shown for a specific placement, nothing will be shown. If auto caching is enabled,
sdk will start to cache another ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement) check if the ad can be shown before calling show method:
```csharp showLineNumbers
if(Appodeal.CanShow(AppodealAdType.Interstitial, "placementName")) {
Appodeal.Show(AppodealShowStyle.Interstitial, "placementName");
}
```
You can configure your impression logic for each placement.
If you have no placements, or call `Appodeal.Show()` method with a placement that does not exist, the impression
will be tagged with `default` placement name and its settings will be applied.
```csharp showLineNumbers
if(Appodeal.canShow(Appodeal.INTERSTITIAL, "placementName")) {
Appodeal.show(Appodeal.INTERSTITIAL, "placementName");
}
```
You can configure your impression logic for each placement.
If you have no placements, or call `Appodeal.show()` method with a placement that does not exist, the impression
will be tagged with `default` placement name and its settings will be applied.
:::caution Placement settings affect **ONLY** ad presentation, not loading or caching.
:::
-------------
## Get Predicted eCPM
This method returns expected eCPM for a currently cached advertisement. The amount is calculated based on
historical data for the current ad unit.
```csharp
Appodeal.GetPredictedEcpm(AppodealAdType.Interstitial);
```
```csharp
Appodeal.getPredictedEcpm(Appodeal.INTERSTITIAL);
```
-------------
## Mute Video Ads
:::caution This method will take effect only on Android platform
:::
You can mute video ads **if calls are muted** on the device. For muting you need to call the following method
before initializing the SDK.
```csharp
Appodeal.MuteVideosIfCallsMuted(true);
```
```csharp
Appodeal.muteVideosIfCallsMuted(true);
```
-------------
---
## Mrec(3)
Mrec is a 300x250 banner. This type can be useful if the application has a large free area for placing
a banner in the interface.
You can use our **demo app** as a reference project.
-------------
## Display
Mrec ads are refreshed every 15 seconds automatically by default.
To display mrec, use the following method:
```csharp
Appodeal.ShowMrecView(yPosition, xPosition, "placementName");
```
Use int value or one of the constants below for `yPosition`:
- `AppodealViewPosition.VerticalTop` — to align a mrec to the top of the screen.
- `AppodealViewPosition.VerticalBottom` — to align a mrec to the bottom of the screen.
Use int value or one of the constants below for `xPosition`:
- `AppodealViewPosition.HorizontalSmart` — to use the full-screen width.
- `AppodealViewPosition.HorizontalCenter` — to center a mrec horizontally.
- `AppodealViewPosition.HorizontalRight` — to align a mrec to the right.
- `AppodealViewPosition.HorizontalLeft` — to align a mrec to the left.
To display mrec, use the following method:
```csharp
Appodeal.showMrecView(yPosition, xPosition, "placementName");
```
Use int value or one of the constants below for `yPosition`:
- `Appodeal.BANNER_TOP` — to align a mrec to the top of the screen.
- `Appodeal.BANNER_BOTTOM` — to align a mrec to the bottom of the screen.
Use int value or one of the constants below for `xPosition`:
- `Appodeal.BANNER_HORIZONTAL_SMART` — to use the full-screen width.
- `Appodeal.BANNER_HORIZONTAL_CENTER` — to center a mrec horizontally.
- `Appodeal.BANNER_HORIZONTAL_RIGHT` — to align a mrec to the right.
- `Appodeal.BANNER_HORIZONTAL_LEFT` — to align a mrec to the left.
:::info Mrec positioning is relative to the top left corner of the screen.
:::
-------------
## Hide Mrec
To hide a mrec ad use the following method:
```csharp
Appodeal.HideMrecView();
```
```csharp
Appodeal.hideMrecView();
```
-------------
## Callbacks
The callbacks are used to track different events in the lifecycle of an ad, e.g. when an ad was clicked on or closed.
Follow the steps below to implement them:
Subscribe to the desired mrec event using one of the options from this
[guide](../advanced/sdk-events). (you can subscribe to any number of events you want)
```csharp
AppodealCallbacks.Mrec.OnLoaded += (sender, args) => { };
```
You will find all existing mrec events in the example below:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.Mrec.OnLoaded += (sender, args) => OnMrecLoaded(args.IsPrecache);
AppodealCallbacks.Mrec.OnFailedToLoad += (sender, args) => OnMrecFailedToLoad();
AppodealCallbacks.Mrec.OnShown += (sender, args) => OnMrecShown();
AppodealCallbacks.Mrec.OnShowFailed += (sender, args) => OnMrecShowFailed();
AppodealCallbacks.Mrec.OnClicked += (sender, args) => OnMrecClicked();
AppodealCallbacks.Mrec.OnExpired += (sender, args) => OnMrecExpired();
}
#region MrecAd Callbacks
// Called when mrec is loaded precache flag shows if the loaded ad is precache)
private void OnMrecLoaded(bool isPrecache)
{
Debug.Log("Mrec loaded");
}
// Called when mrec failed to load
private void OnMrecFailedToLoad()
{
Debug.Log("Mrec failed to load");
}
// Called when mrec is failed to show
private void OnMrecShowFailed()
{
Debug.Log("Mrec show failed");
}
// Called when mrec is shown
private void OnMrecShown()
{
Debug.Log("Mrec shown");
}
// Called when mrec is clicked
private void OnMrecClicked()
{
Debug.Log("Mrec clicked");
}
// Called when mrec is expired and can not be shown
private void OnMrecExpired()
{
Debug.Log("Mrec expired");
}
#endregion
```
1. Extend your class with `IMrecAdListener` interface:
```csharp
class SomeClassName : IMrecAdListener {}
```
2. Implement all required callback methods:
```csharp showLineNumbers
#region MrecAd callback handlers
// Called when mrec is loaded precache flag shows if the loaded ad is precache)
public void onMrecLoaded(bool precache)
{
Debug.Log("Mrec loaded");
}
// Called when mrec failed to load
public void onMrecFailedToLoad()
{
Debug.Log("Mrec failed to load");
}
// Called when mrec is shown
public void onMrecShown()
{
Debug.Log("Mrec shown");
}
// Called when mrec is failed to show
public void onMrecShowFailed()
{
Debug.Log("Mrec show failed");
}
// Called when mrec is clicked
public void onMrecClicked()
{
Debug.Log("Mrec clicked");
}
// Called when mrec is expired and can not be shown
public void onMrecExpired()
{
Debug.Log("Mrec expired");
}
#endregion
```
3. Call the following method:
```csharp
Appodeal.setMrecCallbacks(this);
```
:::note Unity Main Thread
All callbacks are called on native main threads that do not match the main thread of the Unity. If you need to
receive callbacks in the main Unity thread follow our [Callback Usage Guide](../advanced/main-thread-callbacks).
:::
-------------
## Placements
Appodeal SDK allows you to tag each impression with different placement. To use placements, you need to
create placements in Appodeal Dashboard.
[Read more](/advanced/placements) about placements.
To show an ad with placement, you have to call show method with specifying placement's name:
```csharp
Appodeal.ShowMrecView(yPosition, xPosition, "placementName");
```
```csharp
Appodeal.showMrecView(yPosition, xPosition, "placementName");
```
-------------
## Get Predicted eCPM
This method returns expected eCPM for a currently cached advertisement. The amount is calculated based on
historical data for the current ad unit.
```csharp
Appodeal.GetPredictedEcpm(AppodealAdType.Mrec);
```
```csharp
Appodeal.getPredictedEcpm(Appodeal.MREC);
```
-------------
---
## Rewarded Video(3)
Rewarded video is a user-initiated ad type. It allows end-users to get in-app rewards or other benefits
in exchange for viewing a video ad.
You can use our **demo app** as a reference project.
-------------
## Check If Ad Is Loaded
You can check whether or not an ad is loaded at a certain moment. This method returns a boolean value,
representing the rewarded video ad loading status.
```csharp
Appodeal.IsLoaded(AppodealAdType.RewardedVideo);
```
```csharp
Appodeal.isLoaded(Appodeal.REWARDED_VIDEO);
```
:::tip Check Ad Loading Status
We recommend you always check whether an ad is available before trying to show it.
*Example*:
```csharp showLineNumbers
if(Appodeal.IsLoaded(AppodealAdType.RewardedVideo)) {
Appodeal.Show(AppodealShowStyle.RewardedVideo);
}
```
```csharp showLineNumbers
if(Appodeal.isLoaded(Appodeal.REWARDED_VIDEO)) {
Appodeal.show(Appodeal.REWARDED_VIDEO);
}
```
:::
-------------
## Display
To show a rewarded video ad, you need to call the following method:
```csharp
Appodeal.Show(AppodealShowStyle.RewardedVideo);
```
```csharp
Appodeal.show(Appodeal.REWARDED_VIDEO);
```
-------------
## Manual Caching
If you need more control of rewarded video ads loading use manual caching. Manual caching for rewarded video
can be useful to [improve display rate](https://blog.appodeal.com/whats-display-rate/) or decrease
SDK loads when several ad types are cached.
1. To disable automatic caching for rewarded video, use the code below before the SDK initialization:
```csharp
Appodeal.SetAutoCache(AppodealAdType.RewardedVideo, false);
```
```csharp
Appodeal.setAutoCache(Appodeal.REWARDED_VIDEO, false);
```
2. To cache rewarded video ad manually use the following method:
```csharp
Appodeal.Cache(AppodealAdType.RewardedVideo);
```
```csharp
Appodeal.cache(Appodeal.REWARDED_VIDEO);
```
-------------
## Callbacks
The callbacks are used to track different events in the lifecycle of an ad, e.g. when an ad was clicked on or closed.
Follow the steps below to implement them:
Subscribe to the desired rewarded video event using one of the options from this
[guide](../advanced/sdk-events). (you can subscribe to any number of events you want)
```csharp
AppodealCallbacks.RewardedVideo.OnLoaded += (sender, args) => { };
```
You will find all existing rewarded video events in the example below:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.RewardedVideo.OnLoaded += OnRewardedVideoLoaded;
AppodealCallbacks.RewardedVideo.OnFailedToLoad += OnRewardedVideoFailedToLoad;
AppodealCallbacks.RewardedVideo.OnShown += OnRewardedVideoShown;
AppodealCallbacks.RewardedVideo.OnShowFailed += OnRewardedVideoShowFailed;
AppodealCallbacks.RewardedVideo.OnClosed += OnRewardedVideoClosed;
AppodealCallbacks.RewardedVideo.OnFinished += OnRewardedVideoFinished;
AppodealCallbacks.RewardedVideo.OnClicked += OnRewardedVideoClicked;
AppodealCallbacks.RewardedVideo.OnExpired += OnRewardedVideoExpired;
}
#region RewardedVideoAd Callbacks
//Called when rewarded video was loaded (precache flag shows if the loaded ad is precache).
private void OnRewardedVideoLoaded(object sender, AdLoadedEventArgs e)
{
Debug.Log($"[APDUnity] [Callback] OnRewardedVideoLoaded(bool isPrecache:{e.IsPrecache})");
}
// Called when rewarded video failed to load
private void OnRewardedVideoFailedToLoad(object sender, EventArgs e)
{
Debug.Log("[APDUnity] [Callback] OnRewardedVideoFailedToLoad()");
}
// Called when rewarded video was loaded, but cannot be shown (internal network errors, placement settings, etc.)
private void OnRewardedVideoShowFailed(object sender, EventArgs e)
{
Debug.Log("[APDUnity] [Callback] OnRewardedVideoShowFailed()");
}
// Called when rewarded video is shown
private void OnRewardedVideoShown(object sender, EventArgs e)
{
Debug.Log("[APDUnity] [Callback] OnRewardedVideoShown()");
}
// Called when rewarded video is closed
private void OnRewardedVideoClosed(object sender, RewardedVideoClosedEventArgs e)
{
Debug.Log($"[APDUnity] [Callback] OnRewardedVideoClosed(bool finished:{e.Finished})");
}
// Called when rewarded video is viewed until the end
private void OnRewardedVideoFinished(object sender, RewardedVideoFinishedEventArgs e)
{
Debug.Log($"[APDUnity] [Callback] OnRewardedVideoFinished(double amount:{e.Amount}, string name:{e.Currency})");
}
// Called when rewarded video is clicked
private void OnRewardedVideoClicked(object sender, EventArgs e)
{
Debug.Log("[APDUnity] [Callback] OnRewardedVideoClicked()");
}
//Called when rewarded video is expired and can not be shown
private void OnRewardedVideoExpired(object sender, EventArgs e)
{
Debug.Log("[APDUnity] [Callback] OnRewardedVideoExpired()");
}
#endregion
```
1. Extend your class with `IRewardedVideoAdListener` interface:
```csharp
class SomeClassName : IRewardedVideoAdListener {}
```
2. Implement all required callback methods:
```csharp showLineNumbers
#region Rewarded Video callback handlers
//Called when rewarded video was loaded (precache flag shows if the loaded ad is precache).
public void onRewardedVideoLoaded(bool isPrecache)
{
Debug.Log("RewardedVideo loaded");
}
// Called when rewarded video failed to load
public void onRewardedVideoFailedToLoad()
{
Debug.Log("RewardedVideo failed to load");
}
// Called when rewarded video was loaded, but cannot be shown (internal network errors, placement settings, etc.)
public void onRewardedVideoShowFailed()
{
Debug.Log("RewardedVideo show failed");
}
// Called when rewarded video is shown
public void onRewardedVideoShown()
{
Debug.Log("RewardedVideo shown");
}
// Called when reward video is clicked
public void onRewardedVideoClicked()
{
Debug.Log("RewardedVideo clicked");
}
// Called when rewarded video is closed
public void onRewardedVideoClosed(bool finished)
{
Debug.Log("RewardedVideo closed");
}
// Called when rewarded video is viewed until the end
public void onRewardedVideoFinished(double amount, string name)
{
Debug.Log("RewardedVideo finished");
}
//Called when rewarded video is expired and can not be shown
public void onRewardedVideoExpired()
{
Debug.Log("RewardedVideo expired");
}
#endregion
```
3. Call the following method:
```csharp
Appodeal.setRewardedVideoCallbacks(this);
```
:::note Unity Main Thread
All callbacks are called on native main threads that do not match the main thread of the Unity. If you need to
receive callbacks in the main Unity thread follow our [Callback Usage Guide](../advanced/main-thread-callbacks).
:::
-------------
## Placements
Appodeal SDK allows you to tag each impression with different placement. To use placements, you need to
create placements in Appodeal Dashboard.
[Read more](/advanced/placements) about placements.
To show an ad with placement, you have to call show method with specifying placement's name:
```csharp
Appodeal.Show(AppodealShowStyle.RewardedVideo, "placementName");
```
```csharp
Appodeal.show(Appodeal.REWARDED_VIDEO, "placementName");
```
If the loaded ad can’t be shown for a specific placement, nothing will be shown. If auto caching is enabled,
sdk will start to cache another ad, which can affect display rate. To save the loaded ad for future use
(for instance, for another placement) check if the ad can be shown before calling show method:
```csharp showLineNumbers
if(Appodeal.CanShow(AppodealAdType.RewardedVideo, "placementName")) {
Appodeal.Show(AppodealShowStyle.RewardedVideo, "placementName");
}
```
You can configure your impression logic for each placement.
If you have no placements, or call `Appodeal.Show()` method with a placement that does not exist, the impression
will be tagged with `default` placement name and its settings will be applied.
```csharp showLineNumbers
if(Appodeal.canShow(Appodeal.REWARDED_VIDEO, "placementName")) {
Appodeal.show(Appodeal.REWARDED_VIDEO, "placementName");
}
```
You can configure your impression logic for each placement.
If you have no placements, or call `Appodeal.show()` method with a placement that does not exist, the impression
will be tagged with `default` placement name and its settings will be applied.
:::caution Placement settings affect **ONLY** ad presentation, not loading or caching.
:::
-------------
## Server-to-Server Callbacks
To secure your apps economy we offer S2S reward callbacks. To validate each reward, you need to set up a callback URL
on your server that will receive the reward information. We will pass the user data to your callback URL,
which you will need to validate and adjust the user balance accordingly.
1. Create the reward callback URL on your server that will receive the reward information.
2. Fill the created URL and the encryption key in the app settings in your dashboard.
3. The reward callback will be sent to your URL using GET request with two parameters:
```as3
{http:/example.com/reward}?data1={data1}&data2={data2}
```
4. Your URL should decrypt the data and validate it.
5. Check `impression_id` for uniqueness and store it in your system to prevent duplicate transactions.
To set user ID, use the following method before SDK initialization:
```csharp
Appodeal.SetUserId("user#123");
```
```csharp
Appodeal.setUserId("user#123");
```
We offer sample scripts in Go, PHP, Ruby, Java, Node.js, Python 3 and C# to decrypt the data.
If you need samples in other languages, please contact our support team and we will provide them to you.
Sample in PHP: [reward.php](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.php).
Sample in Ruby: [reward.rb](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.rb).
Sample in Java: [reward.java](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/Reward.java).
Sample in Node.js: [reward.js](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.js).
Sample in Python 3: [reward.py](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/reward.py).
Sample in C#: [reward.cs](https://s3-us-west-1.amazonaws.com/appodeal-android/reward/Reward.cs).
Sample in Go: [reward.go](https://appodeal-android.s3-us-west-1.amazonaws.com/reward/reward.go).
-------------
## Getting Reward Data For A Specific Placement
To get reward data and notify your users of it before the video ad is shown, use this method. It returns
`KeyValuePair` with the currency type and amount of the reward.
```csharp
Appodeal.GetRewardParameters("placementName");
```
```csharp
Appodeal.getRewardParameters("placementName");
```
-------------
## Get Predicted eCPM
This method returns expected eCPM for a currently cached advertisement. The amount is calculated based on
historical data for the current ad unit.
```csharp
Appodeal.GetPredictedEcpm(AppodealAdType.RewardedVideo);
```
```csharp
Appodeal.getPredictedEcpm(Appodeal.REWARDED_VIDEO);
```
-------------
## Mute Video Ads
:::caution This method will take effect only on Android platform
:::
You can mute video ads **if calls are muted** on the device. For muting you need to call the following method
before initializing the SDK.
```csharp
Appodeal.MuteVideosIfCallsMuted(true);
```
```csharp
Appodeal.muteVideosIfCallsMuted(true);
```
-------------
---
## Adjust(3)
The Appodeal SDK gives you tools to grow your mobile apps & games. Adjust is one of them.
Use the Adjust account to track your attribution & analytics metrics from your UA campaigns.
Evaluate your soft launch and other marketing campaigns from the Appodeal Reports page that you will find
inside your Appodeal Dashboard.
- Compare Ads vs. IAPs vs. subscription revenues
- Get Forecasted LTV based on UA campaigns
- Find out which Ad Creatives bring top-paying users
- Sync your retention metrics with your ARPU & revenues
- Build deep granular reports to find out new growth opportunities
We have two options for linking Adjust:
- **Our Adjust account.**
:::info
There is a limit of 10 000 non-organic installs per month.
If you are planning to run UA campaigns in near future, you can link our Adjust account.
:::
- **Your own Adjust account.**
------------------------
## Integration Steps
To connect with Adjust, follow the steps:
Step 1. Import Adjust
Complete all the steps from our [integration guide](../get-started).
Go to *Appodeal → Plugin Configuration* and make sure Adjust is included.
Step 2. Contact Us
Contact our support team via live chat or via email [support@appodeal.com](support@appodeal.com) with the following information:
- The desired option.
- Links to the apps in store, which you want to connect.
- Traffic sources, where you are planning to run UA campaigns.
Support team will finish your Adjust integration from Appodeal side and let you know.
:::info Make sure you have all the following features on your account before the connection:
- CSV uploads (from Business plan)
- Cost reporting (from Custom plan)
- Kpi-service (from Custom plan)
:::
Step 1. Import Adjust
Complete all the steps from our [integration guide](../get-started).
Go to *Appodeal → Plugin Configuration* and make sure Adjust is included.
Step 2. Add Your Adjust Account
Add your Adjust account to Appodeal [here](https://app.appodeal.com/integrations/user_acquisition).
You will need to enter your Account name and User Token from Adjust.
User Token - your Api Token on Adjust side.
You can check Raw Data export to Amazon s3 box if you have your AWS (if you don't have it leave this box unchecked).
Step 3. Add Your App On Adjust Side
1. Add the following information:
- App name
- Platform (add your app bundle id)
- Reporting currency (USD is preferable)
2. Create your app
3. Go to *all Settings → S2S Security → Create token & Activate S2S Authentication*
Save this S2S Security Token for the next step
Step 4. Turn On Adjust In Attribution Settings
Go to your app settings in your Appodeal account and choose **Attribution Settings**.
**Primary MMP Account** - your MMP account from where we can get attribution data.
**Secondary MMP Account** (optional) - this option is needed if you transfer from one MMP account to another
or if you want to test two different MMP's.
**Raw Data Source** - the source of raw data.
For Primary MMP Account choose your Adjust account, you can leave Secondary MMP Account empty,
for Raw Data Source choose Amazon S3 Bucket (if you have one and if you have added it with your Adjust account)
or choose Global Callback (enabled by default).
For Attribution Platform choose Adjust, Adjust S2S Security Token (can be copied from Adjust *App Settings → S2S
Security* from the previous step) and Adjust App Token is in your app settings, choose Production for Adjust Environment.
Step 5. Add Global Callback On Adjust Side
Go to your Adjust *account app settings → Raw Data Export → Real-Time Callbacks → Add Global Callback*
(the one copied in Step 4)
Step 6. Create Required Events On Adjust Side
If you use your own Adjust account, you need to add required events according to this
[guide](../advanced/event-tracking) so that in-app purchases will work correctly.
Step 7. Set Up Traffic Sources On Adjust Side
In order to see data of your campaign, you need to set up your traffic sources on Adjust side such as
[Meta](https://help.adjust.com/en/article/skad-facebook-integration) or
[Google](https://help.adjust.com/en/article/skad-google-integration) for example. You can use
[this preset](https://app.appodeal.com/analytics/reports?q=(f~(INSTALL*_DATE~(from~*2022-01-19~to~*2022-01-25)ATTRIBUTION*_NETWORK*_HID~!*333266372425416704)g~!ATTRIBUTION*_AD*_SET*_HID~~m~!installs~(id~retention*_rate~d~1)(id~retention*_rate~d~3)avg*_full*_time*_per*_user*_per*_day~avg*_full*_session*_length~(id~cumulative*_ad*_arpu~d~0)(id~cumulative*_ad*_arpu~d~3)~view~table~fv~*)~&trace=aab07f79934bd99d)
to check the statistics of your UA campaign.
Step 8. Turn On Ad Spend Tracking
In order to be able to see Ad Spend data make sure to complete the steps from this
[guide](https://help.adjust.com/en/article/ad-spend) and link your traffic source account to Adjust.
------------------------
## Demo Application
You can use our **demo app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users to group by the
fact of purchasing in-apps. This will help you adjust the ads for such users or turn them off if needed. In order to
track in-app purchases, please refer to [this guide](../advanced/event-tracking).
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## AppsFlyer(3)
:::note Before the start
AppsFlyer is available for linking only with your own AppsFlyer account with Premium Plan.
Make sure you have the following features:
- [DataLocker](https://support.appsflyer.com/hc/en-us/articles/360000877538-Data-Locker-for-Advertisers).
Data Locker writes your report data to cloud storage for loading into your BI systems.
- [Master API](https://support.appsflyer.com/hc/en-us/articles/213223166-Using-Master-API-campaign-performance-KPIs).
Get selected LTV, activity, Protect360, and retention campaign performance KPIs by API, in CSV or JSON format.
Select 1 or more apps.
These features are available on **AppsFlyer Premium Plan**.
Contact our support team via live chat or via email [support@appodeal.com](mailto:support@appodeal.com) to enable
Attribution Settings needed in step 4, this feature is absolutely free.
:::
AppsFlyer is a mobile marketing, analytics, and attribution platform.
With one connection of AppsFlyer you will be able to see all UA metrics directly in our BI, without using MMP,
analyze them in various sections, and also get access to LTV forecasting.
Note that we also support [forecast metrics](/reporting/revenue-forecast), which will be available by
default with the current integration.
------------------------
## Integration Steps
To connect with AppsFlyer, follow the steps:
Step 1. Import AppsFlyer
Complete all the steps from our [integration guide](../get-started).
Go to *Appodeal → Plugin Configuration* and make sure AppsFlyer is included.
Step 2. Add Your AppsFlyer Account
Add your AppsFlyer account to Appodeal [here](https://app.appodeal.com/integrations/user_acquisition).
You will need to enter:
- Your Account name
- Master API Token (can be found in your *AppsFlyer account → API tokens* [here](https://hq1.appsflyer.com/account/api-tokens))
- Data for Amazon s3 bucket, you can find it in [DataLocker](https://hq1.appsflyer.com/datalocker/overview)
Step 3. Set Up DataLocker
You need to set up DataLocker according to
[this guide](https://support.appsflyer.com/hc/en-us/articles/360000877538?utm_source=hq1&utm_medium=referral#set-up-data-locker)
on AppsFlyer side.
Make sure to indicate fields and report types.
Here is the required minimum for fields
Advertising ID (advertising_id)
Ad (af_ad)
Ad ID (af_ad_id)
Ad Type (af_ad_type)
Adset Name (af_adset)
Adset ID (af_adset_id)
Attribution Lookback Window (af_attribution_lookback)
Campaign ID (af_c_id)
Channel (af_channel)
Cost Currency (af_cost_currency)
Cost Model (af_cost_model)
Cost Value (af_cost_value)
Keywords (af_keywords)
Partner (af_prt)
Reengagement Window (af_reengagement_window)
Site ID (af_siteid)
Sub Param 1 (af_sub1)
Sub Param 2 (af_sub2)
Sub Param 3 (af_sub3)
Sub Param 4 (af_sub4)
Sub Param 5 (af_sub5)
Sub Site ID (af_sub_siteid)
Web ID (af_web_id)
Amazon Fire ID (amazon_aid)
Android ID (android_id)
App ID (app_id)
App Name (app_name)
App Version (app_version)
AppsFlyer ID (appsflyer_id)
Attributed Touch Time (attributed_touch_time)
Attributed Touch Type (attributed_touch_type)
Blocked Reason (blocked_reason)
Blocked Reason Rule (blocked_reason_rule)
Blocked Reason Value (blocked_reason_value)
Blocked Sub Reason (blocked_sub_reason)
Bundle ID (bundle_id)
Campaign (campaign)
Carrier (carrier)
City (city)
Contributor1 Partner (contributor_1_af_prt)
Contributor1 Campaign (contributor_1_campaign)
Contributor1 Match Type (contributor_1_match_type)
Contributor1 Media Source (contributor_1_media_source)
Contributor1 Touch Time (contributor_1_touch_time)
Contributor1 Touch Type (contributor_1_touch_type)
Contributor2 Partner (contributor_2_af_prt)
Contributor2 Campaign (contributor_2_campaign)
Contributor2 Match Type (contributor_2_match_type)
Contributor2 Media Source (contributor_2_media_source)
Contributor2 Touch Time (contributor_2_touch_time)
Contributor2 Touch Type (contributor_2_touch_type)
Contributor3 Partner (contributor_3_af_prt)
Contributor3 Campaign (contributor_3_campaign)
Contributor3 Match Type (contributor_3_match_type)
Contributor3 Media Source (contributor_3_media_source)
Contributor3 Touch Time (contributor_3_touch_time)
Contributor3 Touch Type (contributor_3_touch_type)
Country Code (country_code)
Custom Data (custom_data)
Customer User ID (customer_user_id)
Deeplink URL (deeplink_url)
Device Category (device_category)
Device Download Time (device_download_time)
Device Type (device_type)
DMA (dma)
Event Name (event_name)
Event Revenue (event_revenue)
Event Revenue Currency (event_revenue_currency)
Event Revenue USD (event_revenue_usd)
Event Source (event_source)
Event Time (event_time)
Event Value (event_value)
Google Play Broadcast Referrer (gp_broadcast_referrer)
Google Play Click Time (gp_click_time)
Google Play Install Begin Time (gp_install_begin)
Google Play Referrer (gp_referrer)
HTTP Referrer (http_referrer)
IDFA (idfa)
IDFV (idfv)
IMEI (imei)
Adrevenue Impressions (impressions)
Install App Store (install_app_store)
Install Time (install_time)
IP (ip)
Is Primary Attribution (is_primary_attribution)
Is Receipt Validated (is_receipt_validated)
Is Retargeting (is_retargeting)
Keyword Match Type (keyword_match_type)
Language (language)
Match Type (match_type)
Media Source (media_source)
Adrevenue Mediation Network (mediation_network)
Adrevenue Network (monetization_network)
Network Account ID (network_account_id)
OAID (oaid)
Operator (operator)
Original URL (original_url)
OS Version (os_version)
Adrevenue Placement (placement)
Platform (platform)
Postal Code (postal_code)
Region (region)
Retargeting Conversion Type (retargeting_conversion_type)
SDK Version (sdk_version)
Adrevenue Segment (segment)
State (state)
User Agent (user_agent)
Web Event Type (web_event_type)
WIFI (wifi)
Here is the required minimum for report types:
Step 4. Turn On AppsFlyer In Attribution Settings
Go to your app settings in your Appodeal account and choose **Attribution Settings**.
**Primary MMP Account** - your MMP account from where we can get attribution data.
**Secondary MMP Account** (optional) - this option is needed if you transfer from one MMP account to another
or if you want to test two different MMP's.
For Primary MMP Account choose your AppsFlyer account, you can leave Secondary MMP Account empty.
For Attribution Platform choose AppsFlyer, Dev Key can be found in your app settings on AppsFlyer side and App ID
is on the top of the page in the browser link when you choose app in your AppsFlyer account.
Step 5. Set Up Traffic Sources
In order to see data of your campaign you need to setup your traffic sources such as
[Meta](https://support.appsflyer.com/hc/en-us/articles/207033826-Facebook-Ads-integration-setup) or
[Google](https://support.appsflyer.com/hc/en-us/articles/115002504686-Google-Ads-AdWords-integration-setup),
for example. You can use
[this preset](https://app.appodeal.com/analytics/reports?q=(f~(INSTALL*_DATE~(from~*2022-01-19~to~*2022-01-25)ATTRIBUTION*_NETWORK*_HID~!*333266372425416704)g~!ATTRIBUTION*_AD*_SET*_HID~~m~!installs~(id~retention*_rate~d~1)(id~retention*_rate~d~3)avg*_full*_time*_per*_user*_per*_day~avg*_full*_session*_length~(id~cumulative*_ad*_arpu~d~0)(id~cumulative*_ad*_arpu~d~3)~view~table~fv~*)~&trace=aab07f79934bd99d)
to check the statistics of your UA campaign.
Step 6. Send Ad Revenue Data To AppsFlyer (Optional)
If you want to send ad revenue data to AppsFlyer, then you need to complete the following steps:
- Contact us via email [support@appodeal.com](mailto:support@appodeal.com) or the live chat and we will enable
ad revenue sending
- Complete the steps from this [guide](https://support.appsflyer.com/hc/en-us/articles/360004404977-Appodeal-campaign-configuration-)
and make sure to enable **Get Ad Revenue Data** in *Configuration → Integrated Partners → Appodeal → Ad Revenue*.
Step 7. Set Up Deep Linking Powered By OneLink (Optional)
OneLink allows you to create thousands of links easily.
You can create links with attribution, redirection, and deep linking capabilities that convert paid users into
app users, regardless of device, operating system, platform and etc.
Please use this [guide](https://support.appsflyer.com/hc/en-us/articles/115005248543-Customer-experience-and-deep-linking-overview)
to setup Deep Linking.
:::note
If you have any questions while integrating, feel free to contact us via email
[support@appodeal.com](mailto:support@appodeal.com) or the live chat.
:::
------------------------
## Demo Application
You can use our **demo app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users to group by the
fact of purchasing in-apps. This will help you adjust the ads for such users or turn them off if needed. In order to
track in-app purchases, please refer to [this guide](../advanced/event-tracking).
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Firebase(3)
Firebase SDK (firebase-analytics and firebase-config) is used for analytics and remote config for tests and settings.
------------------------
## Firebase Connection
To connect your Firebase account, follow the steps below.
Step 1. Import Firebase
Firebase SDK is already included in Appodeal SDK (firebase-analytics and firebase-config). You don't need
to install it separately.
### Step 2. Configure Firebase App
1. Follow this [guide](https://firebase.google.com/docs/ios/setup) to configure your Firebase app.
2. **For Android:**
Add your `google-services.json` file from the Firebase console to the Assets folder of your project.
**For iOS:**
Add your `GoogleService-Info.plist` file from the Firebase console to the Assets folder of your project.
3. Go to *Appodeal → Appodeal Settings → Firebase Settings* and tick **Enable auto Configuration** for Firebase.
1. Follow this [guide](https://firebase.google.com/docs/ios/setup) to configure your Firebase app.
2. Follow the steps below:
**For Android**
1. Create an empty folder with name **values** in the following path *Assets/Plugins/Android/appodeal.androidlib/res/*.
2. Use the [online tool](https://dandar3.github.io/android/google-services-json-to-xml.html) to generate
`google-services.xml` file and add it to *Assets/Plugins/Android/appodeal.androidlib/res/values/* folder.
Or you can create an empty `google-services.xml` file and paste the content below (don't forget to replace
the bold data with yours from `google-services.json` file for unity app):
```xml title="google-services.xml" showLineNumbers
// highlight-next-line
Your_project_number_from_google-services.json
// highlight-next-line
Your_storage_bucket_from_google-services.json
// highlight-next-line
Your_project_id_from_google-services.json
// highlight-next-line
Your_current_key_from_google-services.json
// highlight-next-line
Your_current_key_from_google-services.json
// highlight-next-line
Your_mobilesdk_app_id(for unity app)_from_google-services.json
// highlight-next-line
Your_client_id_from_google-services.json
```
### Step 3. Set Up Firebase Remote Config In Attribution Settings (Optional)
If you want to use Firebase Remote Config in your app, you can add your Firebase parameter keys from
*Firebase console → Project name → Remote Config* to **Firebase Config Keys** in Attribution Settings.
### Step 4. Enable Firebase Tracking In Attribution Settings
To enable sending events to Firebase SDK, you need to enable Firebase Tracking in Attribution Settings.
------------------------
## Demo Application
You can use our **demo app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## Meta(3)
Meta SDK (facebook-core) is used for UA (User Acquisition).
:::note
If you are integrating Meta to see UA metrics in our Dashboard, it will work only in connection with Adjust/AppsFlyer.
To connect them, follow [this guide](./adjust) for Adjust and [this guide](./appsflyer) for AppsFlyer.
:::
------------------------
## Meta connection
To connect Meta, follow the steps below.
### Step 1. Import Meta
Meta SDK is already included in Appodeal SDK (facebook-core). You don't need to install it separately.
### Step 2. Configure Meta App
1. Follow this [guide](https://developers.facebook.com/docs/app-events/getting-started-app-events-ios) to
configure you Meta app.
2. Go to *Appodeal → Appodeal Settings → Facebook Settings* and tick **Enable auto configuration**.
3. **For Android:**
- Enter your **App ID**
- Enter your **Facebook Client Token**
**For iOS**
- Enter your **App ID**
- Enter your **Facebook Client Token**
1. Follow this [guide](https://developers.facebook.com/docs/app-events/getting-started-app-events-ios) to
configure you Meta app.
2. Add the following keys for each platform:
**For Android:**
- Add your **App ID**
- Add your **Facebook Client Token**
:::note
Use this format for App ID: **fb**APP-ID
:::
Add a `meta-data` elements to the application element in your `AndroidManifest.xml` file:
```xml title="AndroidManifest.xml" showLineNumbers
```
**For iOS:**
- Add your **App ID**
- Add your **Facebook Client Token**
Configure the `Info.plist` file with an XML snippet that contains data about your app.
1. Right-click `Info.plist`, and choose *Open As ▸ Source Code*.
2. Copy and paste the following XML snippet into the body of your file (`...`).
```xml title="Info.plist" showLineNumbers
CFBundleURLTypesCFBundleURLSchemes
// highlight-next-line
fbAPP-IDFacebookAppID
// highlight-next-line
APP-IDFacebookClientToken
// highlight-next-line
CLIENT-TOKENFacebookDisplayName
// highlight-next-line
APP-NAME
```
:::note You can find Facebook Client Token here:
On [Facebook Developer](https://developers.facebook.com/apps/): choose
*your application → Settings → Advanced → Client Token*
:::
### Step 3. Enable Meta Tracking In Attribution Settings
1. You need to go to your app settings in your Appodeal account and choose **Attribution Settings**.
2. In Meta Settings enable **Meta Tracking**.
------------------------
## Demo Application
You can use our **demo app** as a reference project.
## Track In-app Purchases
Tracks in-app purchase information and sends info to Appodeal servers for analytics. It allows users
to group by the fact of purchasing in-apps. This will help you adjust the ads for such users or turn
them off if needed. In order to track in-app purchases, please refer to [**this guide**](../advanced/in-app-purchases)
## Event Tracking
Appodeal SDK allows you to send events to analytic services such as:
- [Firebase](./firebase),
- [AppsFlyer](./appsflyer),
- [Adjust](./adjust)
- [Meta](./meta).
In order to setup event tracking please refer to [this guide](../advanced/event-tracking).
------------------------
---
## App Tracking Transparency(Data-protection)
Starting in iOS 14.5, IDFA will be unavailable until an app calls the
[App Tracking Transparency](https://developer.apple.com/documentation/apptrackingtransparency)
framework to present the app-tracking authorization request to the end-user. If an app does not present this
request, the IDFA will automatically be zeroed out, which may lead to a significant loss in ad revenue.
To display the App Tracking Transparency authorization request for accessing the IDFA, update your `Info.plist`
to add the `NSUserTrackingUsageDescription` key with a custom message describing the usage.
```xml
NSUserTrackingUsageDescriptionThis identifier will be used to deliver personalized ads to you.
```
And **AppTrackingTransparency.framework** to your project.
:::note
Appodeal Unity Plugin automatically adds `NSUserTrackingUsageDescription` (if the
[corresponding checkbox](../get-started#other-feature-usage-descriptions)
was ticked in *Appodeal → Appodeal Settings*) and AppTrackingTransparency.framework.
:::
-------------
## 1. Stack Consent Manager
If you are using StackConsentManager framework in your project, no additional steps are required. Authorization
request will be shown for users under **iOS 14.5** and higher after
`-[STKConsentManager showConsentDialogFromRootViewController:delegate:]` method is invoked.
No additional steps are needed. Consent Manager integration remains the
same as in the [GDPR/CCPA section](./gdpr-and-ccpa).
Consent Manager will show ATT request only for users under **iOS 14.5** or higher, you may want to add some notes in
App Review Information section of the app version page in App Store Connect. For example, it can be something like:
*App Tracking Transparency request is only available for users under iOS 14.5 or higher.* This step may be needed
because Apple can reject builds that contain `AppTrackingTransparency.framework`, but do not display ATT requests at
app launch.
-------------
## 2. Manually
Disable ATT request via Appodeal Unity Consent Manger:
```csharp showLineNumbers
_consentManager = ConsentManager.GetInstance();
_consentManager?.RequestConsentInfoUpdate(AppKey, this);
// Prevent consent manager to ask app tracking transparency permissions
_consentManager?.DisableAppTrackingTransparencyRequest();
```
```csharp showLineNumbers
consentManager = ConsentManager.getInstance();
consentManager?.requestConsentInfoUpdate(appKey, this);
// Prevent consent manager to ask app tracking transparency permissions
consentManager?.disableAppTrackingTransparencyRequest();
```
Download **Unity App Tracking Transparency Plugin**
Import **Unity App Tracking Transparency Plugin** to your project.
Extend your class with `IAppodealAppTrackingTransparencyListener`:
```csharp
SomeClassName : IAppodealAppTrackingTransparencyListener {}
```
Call the method below to present the App Tracking Transparency authorization request alert. Call this method
at the application launch event
```csharp
AppodealAppTrackingTransparency.RequestTrackingAuthorization(this);
```
Now you can use the following callback methods within your `class`:
```csharp showLineNumbers
public void AppodealAppTrackingTransparencyListenerNotDetermined()
{
Debug.Log("AppodealAppTrackingTransparencyListenerNotDetermined");
}
public void AppodealAppTrackingTransparencyListenerRestricted()
{
Debug.Log("AppodealAppTrackingTransparencyListenerRestricted");
}
public void AppodealAppTrackingTransparencyListenerDenied()
{
Debug.Log("AppodealAppTrackingTransparencyListenerDenied");
}
public void AppodealAppTrackingTransparencyListenerAuthorized()
{
Debug.Log("AppodealAppTrackingTransparencyListenerAuthorized");
}
```
-------------
---
## COPPA(3)
For purposes of the
[Children's Online Privacy Protection Act (COPPA)](http://business.ftc.gov/privacy-and-security/children%27s-privacy)
there is a setting called childDirectedTreatment. If your app is designed for kids you can disable sending user
data to ad networks by calling the method below.
Should be called before the SDK initialization:
```csharp
Appodeal.SetChildDirectedTreatment(bool value);
```
:::info
Call `SetChildDirectedTreatment()` method with `true` to indicate that you want your content treated as child-directed
for purposes of COPPA.
Call `SetChildDirectedTreatment()` method with `false` to indicate that you don't want your content treated as
child-directed for purposes of COPPA.
:::
```csharp
Appodeal.setChildDirectedTreatment(bool value);
```
:::info
Call `setChildDirectedTreatment()` method with `true` to indicate that you want your content treated as child-directed
for purposes of COPPA.
Call `setChildDirectedTreatment()` method with `false` to indicate that you don't want your content treated as
child-directed for purposes of COPPA.
:::
-------------
---
## GDPR & CCPA
:::info
Keep in mind that it’s best to contact qualified legal professionals, if you haven’t done so already, to get more
information and be well-prepared for compliance.
:::
[The General Data Protection Regulation](https://gdpr-info.eu/), better known as GDPR, took effect on May 25, 2018.
It’s a set of rules designed to give EU citizens more control over their personal data. Any *businesses established
in the EU or with users based in Europe are required to comply with GDPR or risk facing heavy fines*. The California
Consumer Privacy Act (CCPA) went into effect on January 1, 2020. **We have put together some guidelines to help
publishers understand better the steps they need to take to be GDPR compliant.**
:::info You can learn more about GDPR and CCPA and their differences [here](https://iapp.org/resources/article/ccpa-and-gdpr-comparison-chart/).
:::
-------------
## Step 1. Update Privacy Policy
### Include Additional Information To Your Privacy Policy
Don’t forget to add information about IP address and advertising ID collection, as well as
[the link to Appodeal’s privacy policy](https://www.appodeal.com/privacy-policy)
to your app’s privacy policy on the App Store.
To speed up the process, you could use
[privacy policy generators](https://app-privacy-policy-generator.firebaseapp.com/) -
just insert advertising ID, IP address, and location (if you collect users’ location) in the **Personally Identifiable
Information you collect** field (in line with other information about your app) and
[the link to Appodeal’s privacy policy](https://www.appodeal.com/privacy-policy)
in the **Link to the privacy policy of third party service providers used by the app** field.
### Add A Privacy Policy To Your Mobile App
You must add your explicit privacy policies in two places: on your app’s Store Listing page and within your app.
You can find detailed instructions on adding your privacy policy to your app on legal service websites.
For example, Iubenda, the solution tailored to legal compliance, provides
[a comprehensive guide](https://www.iubenda.com/en/help/401-privacy-policy-for-ios-and-macos-apps)
on including a privacy policy in your app.
Make sure that your privacy policy website has an SSL certificate—this point might seem obvious,
but it’s still essential.
Here are two useful resources that you can utilize while working on your app compliance:
- [Privacy, Security and Deception regulations (by Google Play)](https://play.google.com/intl/en-GB_ALL/about/privacy-security-deception/user-data)
- [Recommendations on Developing a Meaningful Privacy Policy (by Attorney General California Department of Justice)](https://oag.ca.gov/sites/all/files/agweb/pdfs/cybersecurity/making_your_privacy_practices_public.pdf)
:::note
Please note that although we’re always eager to back you up with valuable information, we’re not authorized
to provide any legal advice. It’s important to address your questions to lawyers who specialize in this area.
:::
-------------
## Step 2. Configure Stack Consent Manager with TCF v2 Support
:::info
Since `Appodeal SDK 3.2.1` it is fully compatible with Google UMP and supports IAB TCF v2.
:::
In order for Appodeal and our ad providers to deliver ads that are more relevant to your users, as a mobile app
publisher, you need to collect explicit user consent in the regions covered by GDPR.
To get consent for collecting personal data of your users, we suggest you use a ready-made solution -
Stack Consent Manager based on **Google User Messaging Platform (UMP)**.
:::note Configure Google UMP
Before you start, you need to configure Google UMP. Follow [this instruction](/advanced/google-cmp-and-tcfv2-support) to setup a consent form.
:::
## Step 3. Integrate Stack Consent Manager
Stack Consent Manager comes with a pre-made consent window that you can easily present to your users.
That means you no longer need to create your own consent window.
:::info Starting from Appodeal SDK 3.0, Stack Consent Manager is included by default.
**Consent will be requested automatically on SDK initialization**, and consent form will be shown if it is
necessary without any additional calls.
Please keep in mind that Consent will be shown only in the **EU** region, you can use VPN for testing.
:::
This means that Appodeal SDK integration code remains the same:
```csharp showLineNumbers
private void Start()
{
int adTypes = AppodealAdType.Interstitial | AppodealAdType.Banner | AppodealAdType.RewardedVideo | AppodealAdType.Mrec;
//highlight-next-line
string appKey = "YOUR_APPODEAL_APP_KEY";
AppodealCallbacks.Sdk.OnInitialized += OnInitializationFinished;
Appodeal.Initialize(appKey, adTypes);
}
#region Initialization Callback
public void OnInitializationFinished(object sender, SdkInitializedEventArgs e) { }
#endregion
```
```csharp showLineNumbers
class Test : IAppodealInitializationListener
{
private void Start()
{
int adTypes = Appodeal.INTERSTITIAL | Appodeal.BANNER | Appodeal.REWARDED_VIDEO | Appodeal.MREC;
//highlight-next-line
string appKey = "YOUR_APPODEAL_APP_KEY";
Appodeal.initialize(appKey, adTypes, this);
}
#region Initialization Callback
public void onInitializationFinished(List<-string-> errors) { }
#endregion
}
```
-------------
## Advanced
If you wish, you can manage and update consent manually using Appodeal CMP Unity Plugin. To do so, first you need
to install the plugin as shown below:
1. Make sure you have Appodeal Unity Plugin v4.2.0 or newer installed via UPM.
2. Copy the link below, head to the *Window → Package Manager → "+" → Add package from git URL*, paste
the copied link there and press enter.
```
https://github.com/appodeal/cmp-unity-plugin.git#v2.1.0
```
3. Import the namespace
```csharp
using AppodealStack.Cmp;
```
### Update Consent Status
To update the consent, call the method:
```csharp showLineNumbers
private void Start()
{
ConsentManager.Instance.OnConsentInfoUpdateFailed += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentInfoUpdateFailed event triggered. Cause: {args.Cause}");
};
ConsentManager.Instance.OnConsentInfoUpdateSucceeded += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentInfoUpdateSucceeded event triggered.");
};
var parameters = new ConsentInfoParameters
{
AppKey = "YOUR_APPODEAL_APP_KEY",
IsUnderAgeToConsent = false,
Sdk = "Appodeal",
SdkVersion = Appodeal.GetNativeSDKVersion()
};
ConsentManager.Instance.RequestConsentInfoUpdate(parameters);
}
```
:::tip
`RequestConsentInfoUpdate` method can be requested at any moment of the application lifecycle. We recommend call
request it at the application launch. Multiple request calls are allowed.
:::
:::note
Required parameters: `YOUR_APPODEAL_APP_KEY` - Appodeal app key, you can get
it [in your personal account](https://app.appodeal.com/apps);
`ConsentInfoParameters` - Data class representing the parameters for a consent update request in the Appodeal
Consent Manager. Use this class to encapsulate the necessary information for updating consent preferences.
Params:
* `AppKey` - The key associated with the user for whom the consent is being updated.
* `IsUnderAgeToConsent` - Optional. Indicates whether the user is tagged for under the age of consent. Set to true
if the user is under the age of consent, otherwise set to false or null.
* `Sdk` - Optional. The identifier for the SDK making the consent update request.
* `SdkVersion` - Optional. The version of the SDK making the consent update request.
`ConsentManager.Instance.OnConsentInfoUpdateFailed`, `ConsentManager.Instance.OnConsentInfoUpdateSucceeded` -
listeners for result request.
:::
### Current Consent Status
After consent info was updated you can check the current consent status:
```csharp
var status = ConsentManager.Instance.ConsentStatus;
```
:::info
Enum class representing the possible consent statuses in the Appodeal Consent Manager.
* `Unknown` - Represents an unknown consent status;
* `Required` - Represents a required consent status;
* `NotRequired` - Represents a not required consent status;
* `Obtained` - Represents an obtained consent status.
:::
### Load Consent Form
You can load and receive `ConsentForm` object using following code:
```csharp showLineNumbers
private ConsentForm consentForm;
ConsentManager.Instance.OnConsentFormLoadFailed += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentFormLoadFailed event triggered. Cause: {args.Cause}");
};
ConsentManager.Instance.OnConsentFormLoadSucceeded += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentFormLoadSucceeded event triggered.");
consentForm = args.ConsentForm;
};
ConsentManager.Instance.Load();
```
### Show Consent Form
After the consent window is ready you can show it.
```csharp showLineNumbers
ConsentManager.Instance.OnConsentFormDismissed += (sender, args) =>
{
string message = "[Appodeal CMP] OnConsentFormDismissed event triggered.";
if (args.Error != null) message += $" Error: {args.Error}";
Debug.Log(message);
consentForm = null;
};
consentForm?.Show();
```
### Load And Show If Required
Alternatively, you can load the form and show it immediately if required.
```csharp showLineNumbers
ConsentManager.Instance.OnConsentFormDismissed += (sender, args) =>
{
string message = "[Appodeal CMP] OnConsentFormDismissed event triggered.";
if (args.Error != null) message += $" Error: {args.Error}";
Debug.Log(message);
};
ConsentManager.Instance.LoadAndShowConsentFormIfRequired();
```
### Revoke Consent
You can reset the consent status to `Unknown`, using method:
```csharp
ConsentManager.Instance.Revoke();
```
### US State Regulations Support (Privacy Entry Point)
:::info Available since Appodeal CMP `2.1.0`.
:::
US state privacy laws (CCPA, CPA, VCDPA, and others) follow an **opt-out model**: data processing is
allowed by default, but users must be given a permanent way to opt out — typically a
**"Do Not Sell or Share My Personal Information"** button (the *Privacy Entry Point*). In the US zone
the consent form is **not** shown automatically on SDK initialization, because consent is not required
at launch — the
opt-out form must be shown on demand, in response to a user tap.
To support this, the Consent Manager exposes two members:
- `ConsentManager.Instance.PrivacyOptionsStatus` — tells you whether you must surface a Privacy Entry
Point button in your app UI.
- `ConsentManager.Instance.ShowPrivacyOptionsForm()` — shows the US opt-out form (or the GDPR
re-consent form when called in the EEA).
Both become available after `RequestConsentInfoUpdate` completes.
#### Check whether a Privacy Entry Point is required
Use `PrivacyOptionsStatus` to decide whether to render the opt-out button. It returns
`PrivacyOptionsStatus.Required` for users in regulated US states and in the EEA (for GDPR re-consent),
`PrivacyOptionsStatus.NotRequired` elsewhere, and `PrivacyOptionsStatus.Unknown` before
`RequestConsentInfoUpdate` has completed.
```csharp
if (ConsentManager.Instance.PrivacyOptionsStatus == PrivacyOptionsStatus.Required)
{
// Show a "Do Not Sell or Share My Personal Information" / Privacy Settings button
}
```
#### Show the Privacy Options form
Call `ShowPrivacyOptionsForm` from the click handler of your Privacy Entry Point button. This is the
**only** way to display the US opt-out form, and it must be triggered by an explicit user interaction —
not on SDK initialization. The result is delivered through the same `OnConsentFormDismissed` event used
by the consent form.
```csharp showLineNumbers
ConsentManager.Instance.OnConsentFormDismissed += (sender, args) =>
{
string message = "[Appodeal CMP] OnConsentFormDismissed event triggered.";
if (args.Error != null) message += $" Error: {args.Error}";
Debug.Log(message);
};
ConsentManager.Instance.ShowPrivacyOptionsForm();
```
:::note
Once the user interacts with the US opt-out form, Stack Consent Manager writes the corresponding
privacy keys (`IABGPP_*`) to the platform's default preferences, where ad networks read them. Before
the form has been shown at least once, these keys remain empty and ad networks may treat the user as
"no consent collected".
:::
### Non-Personalized Advertising
:::info Available since Appodeal Unity Plugin 4.3.0.
:::
Consent is enforced automatically — no extra integration is required.
If you want to request non-personalized advertising regardless of the resolved consent, call
`Appodeal.SetNonPersonalized(true)`. This disables the collection of data used for ad personalization,
and a publisher-set value takes precedence over the consent resolved from the CMP.
Call it before `Appodeal.Initialize(...)`.
This is relevant in several scenarios:
- **Age-restricted users (US).** US state laws (CCPA/CPRA in California, and similar laws in Virginia,
Colorado, Connecticut, and others) restrict selling or sharing the personal data of minors, and COPPA
adds stricter rules for children under 13. Use this flag alongside
[`SetChildDirectedTreatment`](coppa) when you cannot determine the exact age but targeting must be
limited.
- **Users who declined personalized advertising** through your own consent flow, when they are not
subject to a specific regulation covered by the other APIs.
- **General opt-out** — a catch-all to suppress targeting signals when none of the more specific privacy
flags apply.
```csharp
Appodeal.SetNonPersonalized(true);
```
-------------
---
## Ad Revenue Callbacks(3)
Appodeal SDK allows you to get impression-level revenue data with Ad Revenue Callbacks. This data includes information
about network name, revenue, ad type, etc.
The impression-level ad revenue data can be used then to share with your mobile measurement partner of choice, such as
[Firebase](../services/firebase), for all supported networks.
If you have integrated Firebase, which is included in Appodeal SDK, using this [guide](../services/firebase),
then ad revenue data will be sent automatically, you can read more about it
[here](./launching-troas#step-2-set-up-your-firebase-account).
:::info Minimum Requirements:
Appodeal SDK 3.0.1+
:::
## Callback Implementation
1. Subscribe to the Ad Revenue event using one of the options from this [guide](./sdk-events).
2. You can use callbacks as shown below:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.AdRevenue.OnReceived += (sender, args) => {};
}
```
1. Extend your class with `IAdRevenueListener`:
```csharp
SomeClassName : IAdRevenueListener {}
```
2. Implement required callback methods in your class:
```csharp showLineNumbers
#region IAdRevenueListener implementation
public void onAdRevenueReceived(AppodealAdRevenue ad)
{
// Called whenever SDK receives revenue information for an ad
}
#endregion
```
3. Then call the following method **before SDK initialization**:
```csharp
Appodeal.setAdRevenueCallback(this);
```
-------------
:::note Admob Notice
To get impression-level ad revenue from Admob you also need to turn on the setting in your [AdMob account](https://apps.admob.com/v2/settings/account-info).
Go to your **Admob Account Settings** → **Account** → turn on **Impression-level ad revenue toggle**.
:::
## Appodeal Ad Revenue Description
`AppodealAdRevenue` - represents revenue information from the ad network.
| Parameter | Type | Description |
| :--- | :--- | :--- |
| NetworkName | String | The name of the ad network |
| DemandSource | String | The demand source name and bidder name in case of impression from real-time bidding |
| AdUnitName | String | Unique ad unit name |
| Placement | String | Appodeal's placement name |
| Revenue | Double | The ad's revenue amount or 0 if it doesn't exist |
| AdType | String | Appodeal's ad type as string presentation |
| Currency | String | Current currency supported by Appodeal (USD) as string presentation |
| RevenuePrecision | String | The revenue precision |
:::info Revenue Precision options
1. `exact` - programmatic revenue is the resulting price of the auction
2. `publisher_defined` - revenue from cross-promo campaigns
3. `estimated` - revenue based on ad network price floors or historical eCPM
4. `undefined` - revenue amount is not defined
:::
-------------
## Use Case
:::info Please remember:
If you have integrated analytics for example Firebase using this [guide](../services/firebase) with Appodeal,
then no additional steps are required.
:::
In case you are using your own analytics in the project, please find the example below:
```csharp showLineNumbers
#region IAdRevenueListener implementation
public void OnAdRevenueReceived(AppodealAdRevenue ad)
{
//AppsFlyer
var dict = new Dictionary();
dict.Add("AdUnitName", ad.AdUnitName);
dict.Add("AdType", ad.AdType);
AppsFlyerAdRevenue.logAdRevenue(ad.NetworkName,
AppsFlyerAdRevenueMediationNetworkType.AppsFlyerAdRevenueMediationNetworkTypeAppodeal,
ad.Revenue, ad.Currency, dict
);
//Adjust
AdjustAdRevenue adRevenue = new AdjustAdRevenue(AdjustConfig.AdjustAdRevenueSourcePublisher);
adRevenue.setRevenue(ad.Revenue, ad.Currency);
adRevenue.setAdRevenueNetwork(ad.NetworkName);
adRevenue.setAdRevenueUnit(ad.AdUnitName);
Adjust.trackAdRevenue(adRevenue);
//Firebase
Firebase.Analytics.FirebaseAnalytics.LogEvent(
Firebase.Analytics.FirebaseAnalytics.EventAdImpression,
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.ParameterAdPlatform, "Appodeal"),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.ParameterAdFormat, ad.AdType),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.ParameterAdSource, ad.NetworkName),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.AdUnitName, ad.AdUnitName),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.AdCurrency, ad.Currency),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.Value, ad.Revenue)
);
}
#endregion
```
```csharp showLineNumbers
#region IAdRevenueListener implementation
public void onAdRevenueReceived(AppodealAdRevenue ad)
{
//AppsFlyer
var dict = new Dictionary();
dict.Add("AdUnitName", ad.AdUnitName);
dict.Add("AdType", ad.AdType);
AppsFlyerAdRevenue.logAdRevenue(ad.NetworkName,
AppsFlyerAdRevenueMediationNetworkType.AppsFlyerAdRevenueMediationNetworkTypeAppodeal,
ad.Revenue, ad.Currency, dict
);
//Adjust
AdjustAdRevenue adRevenue = new AdjustAdRevenue(AdjustConfig.AdjustAdRevenueSourcePublisher);
adRevenue.setRevenue(ad.Revenue, ad.Currency);
adRevenue.setAdRevenueNetwork(ad.NetworkName);
adRevenue.setAdRevenueUnit(ad.AdUnitName);
Adjust.trackAdRevenue(adRevenue);
//Firebase
Firebase.Analytics.FirebaseAnalytics.LogEvent(
Firebase.Analytics.FirebaseAnalytics.EventAdImpression,
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.ParameterAdPlatform, "Appodeal"),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.ParameterAdFormat, ad.AdType),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.ParameterAdSource, ad.NetworkName),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.AdUnitName, ad.AdUnitName),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.AdCurrency, ad.Currency),
new Firebase.Analytics.Parameter(
Firebase.Analytics.FirebaseAnalytics.Value, ad.Revenue)
);
}
#endregion
```
-------------
---
## Ad Revenue Forwarding to MMP/BI(3)
Appodeal SDK allows you to get ad revenue data using
[Ad Revenue Attribution API](/advanced/ad-revenue-attribution) and
[Ad Revenue Callbacks](./ad-revenue-callback).
This data includes information about network name, revenue, ad type, etc.
It is possible to send ad revenue data to Adjust, AppsFlyer, and also to your own MMP/BI.
To send ad revenue data to MMP/BI, please follow the steps below:
-------------
## Adjust
No additional steps are required if you have integrated Adjust using this [guide](../services/adjust).
Ad revenue data will be sent automatically after the ad impression.
If you want to send ad revenue data to Adjust you need to use the code below :
```
AdjustAdRevenue adRevenue = new AdjustAdRevenue(AdjustConfig.AdjustAdRevenueSourcePublisher);
adRevenue.setRevenue(ad.Revenue, ad.Currency);
adRevenue.setAdRevenueNetwork(ad.NetworkName);
adRevenue.setAdRevenueUnit(ad.AdUnitName);
Adjust.trackAdRevenue(adRevenue);
```
-------------
## AppsFlyer
**Ad Revenue Attribution API**:
- If you want to send ad revenue to AppsFlyer, please follow step 6 from the
[AppsFlyer](../services/appsflyer#step-6-send-ad-revenue-data-to-appsflyer-optional) guide.
**Ad Revenue Callbacks**:
- Please refer to the [guide](./ad-revenue-callback) in this case.
-------------
## Own MMP/BI
**Ad Revenue Attribution API**:
- Contact us via email at [support@appodeal.com](mailto:support@appodeal.com) or the live chat, and we will
enable ad revenue sending
- Send your attribution ID to Appodeal using `Appodeal.SetExtraData()` method from this
[guide](./user-data#send-extra-data)
- To get ad revenue data, you need to follow this [guide](/advanced/ad-revenue-attribution)
- Then you can send received ad revenue data to your MMP/BI
**Ad Revenue Callbacks**:
- Please refer to the [guide](./ad-revenue-callback) in this case.
-------------
---
## App-ads.txt File
The app-ads.txt file is a text file which provides a mechanism for publishers to declare their authorized digital
sellers. Created by [IAB](https://www.iab.com/), it is an extension of the original ads.txt standard that was
used for the same purpose in web advertising. It helps ad networks and DSPs easily verify whether an ad network or
exchange they are buying your traffic from is allowed to sell it.
:::info
Adding app-ads.txt augments trust between advertisers, demand partners, and publishers. If publishers do not have
their own website where they can place an add app-ads.txt file, premium brand demand may be unavailable to them.
:::
**If you don't have app-ads.txt file yet**:
1. Add developer website URL to your Google Play and App Store apps.
2. Download Appodeal's app-ads.txt from [this page](https://app.appodeal.com/user_profile/ads_txt).
3. Upload app-ads.txt file to the root path of your developer website (it should look like this:
`example.com/app-ads.txt`).
**If you already have app-ads.txt**:
1. Copy Appodeal's app-ads.txt from [this page](https://app.appodeal.com/user_profile/ads_txt).
2. Paste the content to your app-ads.txt.
-------------
---
## Appodeal Dependency Manager
Appodeal SDK: **4.0.0+**
## Loading
After integrating the Appodeal package using **UPM distribution** from Appodeal Unity
[documentation](../get-started?distribution=upm#step-1-import-sdk), you will be able to open Appodeal Dependency Manager
using one of the options provided below:
1. Using the shortcut: `option + D` (for macOS) and `alt + D` (for Windows).
2. Open the `Appodeal` tab at the top menu bar of the Unity Editor and select `Dependency Manager > Open`.
3. Open `Project Settings > Appodeal DM` window.
:::info
The Appodeal DM window needs some time to load the required configurations from the server, usually a few seconds.
:::
Once everything has loaded successfully, you will see the Appodeal DM window.
-------------
## Appodeal DM Functionality
The Appodeal DM simplifies the process of managing Appodeal package dependencies.
It consists of three sections: **Mediation Engines**, **Ad Networks**, and **Services**.
#### Mediation Engines
This section displays the available mediation engines inside Appodeal SDK.
Changing the selected mediation version will reset all previously selected ad network and service adapters.
#### Ad Networks
This section shows the ad networks and ad types supported by each network as well as the mediation engines compatible
with them. Depending on the selected SDK version, a different set of compatible mediation engines may be available,
which is reflected graphically in the UI.
#### Services
This section displays the available services inside Appodeal SDK.
:::info
By default, the recommended SDK version is selected for each platform, but you can choose any other available version.
:::
:::info
You can disable a non-required SDK for a single platform by selecting **none** option in the version dropdown,
or disable it entirely for both platforms using the checkbox in the top-left corner.
:::
:::info
You can hover over the `i` icon to get more information about a particular SDK.
:::
:::info Light theme is supported
:::
-------------
### Manage Appodeal Dependencies
After adding the Appodeal package, all the ad networks and services are added by default.
You can use Appodeal DM to update, remove, import, or downgrade dependencies.
When using Appodeal SDK Full Package, please make sure you **haven't excluded** service dependencies.
:::info
We don't recommend excluding any dependencies without any worthwhile reason.
Having as many network dependencies as possible is recommended to increase your fill rate and revenue.
The required dependencies cannot be excluded.
:::
Press the `Default` button at the top of the window and `Generate` at the bottom to import all the recommended dependencies.
When using Appodeal SDK Mediation Only, please make sure to exclude service dependencies as shown below.
Press `Generate` at the bottom.
-------------
### Dependencies Generation Options
Appodeal DM provides four predefined dependency generation options.
- `Default` - all required and recommended SDKs will be selected except optional ones.
- `Custom` - your saved selection from the previous sync will be used.
- `All` - all SDKs will be selected.
- `Minimal` - only the required SDKs will be selected, and the rest will be disabled.
Once dependencies have been generated successfully, you will see the following message.
You will also be able to see detailed logs in the console showing which adapters were added, removed, updated, or downgraded.
-------------
### Appodeal DM Settings
Below you can see the default values of the Appodeal DM Settings window.
It includes the following options:
- `Validate dependencies automatically` - when enabled, it checks once per session
(from opening to closing the Unity Editor) if currently installed adapter versions require any attention.
If we detect that your config is not optimal, you will see the following window.
You can also manually validate the dependencies at any time using one of the options provided below:
1. Using the shortcut: `option + V` (for macOS) and `alt + V` (for Windows).
2. Open the `Appodeal` tab at the top menu bar of the Unity Editor and select `Dependency Manager > Validate Dependencies`.
- `Check for plugin updates` - when enabled, it checks once per session
(from opening to closing the Unity Editor) if updates are available for the currently installed Appodeal Unity Plugin.
- `Include plugin beta versions` - when enabled, it checks for beta versions of the Appodeal Unity Plugin as well.
- `Enable verbose logging` - when enabled, displays additional information about the Appodeal DM work in the console.
- `Open DM Documentation` - redirects you to the current page.
-------------
## FAQ & Troubleshooting
### Appodeal DM Loading Error
In case it is not possible to display Appodeal DM at the moment you will get the error shown below.
Follow the next steps to resolve the issue:
1. Reload the page by pressing the **Reload** button from the above screenshot.
2. If reloading doesn't help contact our support team by pressing
[Contact Support](/faq-and-troubleshooting/troubleshooting/general/technical-support) button.
### Error Generating Dependencies
In case something went wrong while generating the dependencies you will get the error shown below.
Follow the next steps to resolve the issue:
1. Enable verbose logging in our [Appodeal DM Settings](#appodeal-dm-settings).
2. Check the logs in the console for more information and send them to us using
one of [these options](/faq-and-troubleshooting/troubleshooting/general/technical-support).
-------------
---
## Event Tracking(3)
## Introduction
Thanks to in-app events, you can track user activity inside your app. You can keep track of events such as
registration, passing levels, purchases, etc., as in-app events. The implementation of in-app events is mandatory
for all post-install analysis purposes.
## Types Of Events
In-app events can be divided into two categories:
- **Basic in-app events** are standard in-app events that help you understand user activity inside your app.
**Examples:**
```
level1_finished
level2_start
app_login
```
- **Rich in-app events** are the same as basic in-app events but let you get more detailed information about the event
through a number of parameters. You will learn more about them in step 1. Through parameters, you can send additional
information about the event. For example, you can not only learn that app was opened but also the exact date and time.
**Examples:**
```
level1_finished(result)
level2_start(time)
app_login(date)
```
-------------
## Recommended Events
You need to select the events that best suit your application.
:::info Recommendations:
- For better navigation through reports, we recommend using the same event names in your app across all platforms.
- Create all kinds of events with a maximum number of details that describe user actions in your application.
- We recommend using only lower-case alpha-numeric characters (a-z and 0-9) for your in-app event names.
:::
**Examples (for games):**
``` text
game_start
game_win
game_end
main_menu_open
game_lose
round_start
round_end
pause_menu_open
design_dialog_open
settings_dialog_open
design_application_changed
level1_complete
appodeal_consent_dialog_open
appodeal_consent_dialog_result
```
**Examples (for other apps):**
```
appodeal_initialized
complete_registration
user_login
tutorial_completion
on_search
content_view
in_app_purchase
```
-------------
## Step 1. How To Track In-app Events
Appodeal SDK allows you to send events to the following analytic services using a single method:
- [Firebase](../services/firebase)
- [AppsFlyer](../services/appsflyer)
- [Adjust](../services/adjust)
- [Meta](../services/meta)
```csharp
Appodeal.LogEvent("appodeal_sdk_test_event");
```
Send events with params if required.
```csharp showLineNumbers
Appodeal.LogEvent("logEventWithParams",
new Dictionary
{
{ "testKey1", "testParam1" },
{ "testKey2", 42 },
{ "testKey3", 0.42d }
});
```
You can also select any combination of services the event will be sent to:
```csharp
Appodeal.LogEvent("appodeal_sdk_test_event", services:AppodealService.AppsFlyer | AppodealService.Facebook);
```
or with all 3 arguments:
```csharp showLineNumbers
Appodeal.LogEvent("logEventWithParamsToFirebase",
new Dictionary
{
{ "testKey1", "testParam1" },
{ "testKey2", 42 },
{ "testKey3", 0.42d }
},
AppodealService.Firebase);
```
```csharp
Appodeal.logEvent("appodeal_sdk_test_event");
```
Send events with params if required.
```csharp showLineNumbers
Appodeal.logEvent("logEventWithParams",
new Dictionary
{
{ "testKey1", "testParam1" },
{ "testKey2", 42 },
{ "testKey3", 0.42d }
});
```
:::info Please note:
Event parameters can only be strings and numbers, they allow you to send additional information about the event in
your app.
:::
-------------
## Step 2. Configure In-app Events
Some additional steps may be needed on the MMP side to complete events setup.
### Appodeal Adjust Account
- If you want to send events to Adjust, contact our support team via email
[support@appodeal.com](mailto:support@appodeal.com) or in the live chat and send us the list with event names.
By default, Appodeal SDK sends s2s events to Adjust.
The list of s2s events:
- `dc_cpa_event_d0` - this event includes the ARPU of Day 0 after the app install
- `dc_cpa_event_d2` - this event includes the ARPU of Day 2 after the app install
- `dc_cpa_event_d7` - this event includes the ARPU of Day 7 after the app install
- `dc_cpa_event_d30` - this event includes the ARPU of Day 30 after the app install
:::info
If you want to target those s2s events in your UA campaigns, please contact our support team via email
[support@appodeal.com](mailto:support@appodeal.com) or in the live chat so we can connect those events with your
Traffic Source.
:::
-------------
### Own Adjust Account
If you want to send events to Adjust you need to create your events on Adjust side according to this
[guide](https://help.adjust.com/en/article/basic-event-setup) and **send their tokens** to us via email
[support@appodeal.com](mailto:support@appodeal.com) or in the live chat:
- Find your app in the dashboard and select your app options caret (^).
- Select *All Settings → Events*.
- Find the **Create New Event** label at the bottom of the module and enter your event name.
- Select **Create**.
- **Send us the token** of each event specifying the event name (you can find the token right next to the event
in *All Settings → Events*).
You also need to create some required SDK events presented below:
**Required SDK events:**
```
hs_sdk_purchase
hs_sdk_unknown
hs_sdk_purchase_error
```
:::info
- `hs_sdk_purchase` - in-app purchase was validated successfully
- `hs_sdk_unknown` - unknown event
- `hs_sdk_purchase_error` - in-app purchase wasn't validated, error occurred
:::
-------------
### Own AppsFlyer Account
- No additional steps are required.
-------------
---
## In-App Purchases(3)
## Automatic Verification
:::info
Starting Appodeal SDK 3.6.0+, it is possible to automatically verify and submit purchases/subscription to
Appodeal, as well as receive purchase information from the Appodeal SDK using [Appsflyer](../services/appsflyer).
:::
To activate this feature, contact us via email at [support@appodeal.com](mailto:support@appodeal.com)
or the live chat, and ask to enable ad **roi360** feature.
-------------
### Step 1. Accounts Connection
Follow our [Android](/android/advanced/in-app-purchases) and/or [iOS](/ios/advanced/in-app-purchases)
guides to connect all required accounts.
-------------
### Step 2. Optional Callbacks
The callbacks are used to track successful and failed purchases. To implement them, you need to follow
the steps below:
1. Subscribe to the desired Purchase event using one of the options from this [guide](./sdk-events).
(you can subscribe to any event you want)
2. You can use callbacks as shown below:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.Purchase.OnValidationSucceeded += OnPurchaseValidationSucceeded;
AppodealCallbacks.Purchase.OnValidationFailed += OnPurchaseValidationFailed;
}
#region PurchaseValidation Callbacks
private void OnPurchaseValidationSucceeded(object sender, PurchaseValidatedEventArgs e)
{
Debug.Log("Purchase Validation Succeeded");
}
private void OnPurchaseValidationFailed(object sender, PurchaseValidationFailedEventArgs e)
{
Debug.Log("Purchase Validation Failed");
}
#endregion
```
-------------
## Manual Verification
:::info
In-App purchase tracking will work only in connection with Adjust/AppsFlyer. To connect them, follow
[this guide](../services/adjust) for Adjust and [this guide](../services/appsflyer) for AppsFlyer.
:::
It's possible to track in-app purchase information and send info to Appodeal servers for analytics. It allows to
group users by the fact of purchasing in-apps. This will help you to adjust the ads for such users or simply turn it
off, if needed. To make this setting work correctly, please submit the purchase info via the Appodeal SDK.
-------------
### Step 1. Validate In-App Purchases
:::info
If you are using your own Adjust account you need to complete Step 2 from our Event Tracking
[guide](./event-tracking#step-2-configure-in-app-events) and create some required events on Adjust side.
:::
To make this work correctly, please submit the purchase information via Appodeal SDK.
:::info
You need to follow the official Unity IAP
[guide](https://docs.unity3d.com/Packages/com.unity.purchasing@4.9/manual/Overview.html) to set up purchases and
get purchase information.
:::
```csharp showLineNumbers
#if UNITY_ANDROID
var additionalParams = new Dictionary { { "key1", "value1" }, { "key2", "value2" } };
var purchase = new PlayStoreInAppPurchase.Builder(PlayStorePurchaseType.Subs)
.WithAdditionalParameters(additionalParams)
.WithPurchaseTimestamp(793668600)
.WithDeveloperPayload("payload")
.WithPurchaseToken("token")
.WithPurchaseData("data")
.WithPublicKey("key")
.WithSignature("signature")
.WithCurrency("USD")
.WithOrderId("orderId")
.WithPrice("1.99")
.WithSku("sku")
.Build();
Appodeal.ValidatePlayStoreInAppPurchase(purchase, this);
#elif UNITY_IOS
var additionalParams = new Dictionary { { "key1", "value1" }, { "key2", "value2" } };
var purchase = new AppStoreInAppPurchase.Builder(AppStorePurchaseType.Consumable)
.WithAdditionalParameters(additionalParams)
.WithTransactionId("transactionId")
.WithProductId("productId")
.WithCurrency("USD")
.WithPrice("2.89")
.Build();
Appodeal.ValidateAppStoreInAppPurchase(purchase, this);
#endif
```
:::note
**For Android**:
Please make sure if you have created in-app product in *Google Play Console → Monetize* section to use:
- `PlayStorePurchaseType.InApp` for purchase type.
- `PlayStorePurchaseType.Subs` for subscription.
**For iOS**:
Please make sure if you have created in-app product in App Store Connect to use:
- `AppStorePurchaseType.Consumable` or `AppStorePurchaseType.NonConsumable` for purchase type.
- `AppStorePurchaseType.AutoRenewableSubscription` or `AppStorePurchaseType.NonRenewingSubscription` for subscription.
:::
```csharp showLineNumbers
#if UNITY_ANDROID
var additionalParams = new Dictionary { { "key1", "value1" }, { "key2", "value2" } };
var purchase = new PlayStoreInAppPurchase.Builder(Appodeal.PlayStorePurchaseType.Subs)
.withAdditionalParameters(additionalParams)
.withPurchaseTimestamp(793668600)
.withDeveloperPayload("payload")
.withPurchaseToken("token")
.withPurchaseData("data")
.withPublicKey("key")
.withSignature("signature")
.withCurrency("USD")
.withOrderId("orderId")
.withPrice("1.99")
.withSku("sku")
.build();
Appodeal.validatePlayStoreInAppPurchase(purchase, this);
#elif UNITY_IOS
var additionalParams = new Dictionary { { "key1", "value1" }, { "key2", "value2" } };
var purchase = new AppStoreInAppPurchase.Builder(Appodeal.AppStorePurchaseType.Consumable)
.withAdditionalParameters(additionalParams)
.withTransactionId("transactionId")
.withProductId("productId")
.withCurrency("USD")
.withPrice("2.89")
.build();
Appodeal.validateAppStoreInAppPurchase(purchase, this);
#endif
```
:::note
**For Android**:
Please make sure if you have created in-app product in *Google Play Console → Monetize* section to use:
- `Appodeal.PlayStorePurchaseType.Subs` for purchase type.
- `Appodeal.PlayStorePurchaseType.Subs` for subscription.
**For iOS**:
Please make sure if you have created in-app product in App Store Connect to use:
- `Appodeal.AppStorePurchaseType.Consumable` or `Appodeal.AppStorePurchaseType.NonConsumable` for purchase type.
- `Appodeal.AppStorePurchaseType.AutoRenewableSubscription` or `Appodeal.AppStorePurchaseType.NonRenewingSubscription`
for subscription.
:::
| Parameter | Description | Usage |
| :--- | :--- | :--- |
| purchaseType | Purchase type must be InApp or Subs | Adjust/AppsFlyer |
| publicKey | [Public key from Google Developer Console](https://support.google.com/googleplay/android-developer/answer/186113) | AppsFlyer |
| signature | Transaction signature (returned from Google API when the purchase is completed | Adjust/AppsFlyer |
| purchaseData | Product purchased in JSON format (returned from Google API when the purchase is completed | AppsFlyer |
| purchaseToken | Product purchased token (returned from Google API when the purchase is completed) | Adjust |
| purchaseTimestamp | Product purchased timestamp (returned from Google API when the purchase is completed) | Adjust |
| developerPayload | Product purchased developer payload (returned from Google API when the purchase is completed) | Adjust |
| orderId | Product purchased unique order id for the transaction (returned from Google API when the purchase is completed) | Adjust |
| sku | Stock keeping unit id | Adjust |
| price | In-app event revenue | Adjust/AppsFlyer/Appodeal |
| currency | In-app event currency | Adjust/AppsFlyer/Appodeal |
| additionalParameters | Additional parameters of the in-app event | - |
| transactionId | Some transaction id | Adjust/AppsFlyer |
| productId | Some product id | Adjust/AppsFlyer |
:::caution Required Parameters:
**For iOS**
- All parameters are required
**For Android**
- purchaseType
- purchaseToken
- developerPayload
- sku
- price
- currency
- additionalParameters
- purchaseType
- signature
- purchaseToken
- purchaseTimestamp
- orderId
- sku
- price
- currency
- additionalParameters
- purchaseType
- publicKey
- signature
- purchaseData
- price
- currency
- additionalParameters
:::
-------------
### Step 2. Callbacks
The callbacks are used to track successful and failed in-app purchases. To implement them, you need to follow
the steps below:
1. Subscribe to the desired In-App Purchase event using one of the options from this [guide](./sdk-events).
(you can subscribe to any event you want)
2. You can use callbacks as shown below:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.InAppPurchase.OnValidationSucceeded += OnInAppPurchaseValidationSucceeded;
AppodealCallbacks.InAppPurchase.OnValidationFailed += OnInAppPurchaseValidationFailed;
}
#region InAppPurchaseValidation Callbacks
private void OnInAppPurchaseValidationSucceeded(object sender, InAppPurchaseEventArgs e)
{
Debug.Log("In-App Purchase Validation Succeeded");
}
private void OnInAppPurchaseValidationFailed(object sender, InAppPurchaseEventArgs e)
{
Debug.Log("In-App Purchase Validation Failed");
}
#endregion
```
1. Extend your class with `IInAppPurchaseValidationListener` interface:
```csharp
SomeClassName : IInAppPurchaseValidationListener {}
```
2. Implement the required callback methods in your class:
```csharp showLineNumbers
#region InAppPurchaseValidation Callbacks
private void onInAppPurchaseValidationSucceeded(string json)
{
Debug.Log("In-App Purchase Validation Succeeded");
}
private void onInAppPurchaseValidationFailed(string json)
{
Debug.Log("In-App Purchase Validation Failed");
}
#endregion
```
-------------
### Step 3. Generate Json File In Google Cloud (Android Only)
1. Login to [Google Cloud](https://console.developers.google.com/) with your credentials.
2. Select **Google Play Console Developer** project on the top left corner as shown below.
:::note
Please make sure to select **Google Play Console Developer** project at this step instead of your exact app project.
Google Play Console only allows to link **Google Play Console Developer** cloud projects (later on step 3).
:::
3. Select *Credentials → Create Credentials → select Service Account*.
4. Select **Viewer** as a role for Service Account and press **Done**.
5. Go to your service account and press *keys → Add key → choose JSON* and **send us** the JSON file via email
[support@appodeal.com](mailto:support@appodeal.com) or a live chat.
-------------
### Step 4. Add Required Permissions In Google Play Console (Android Only)
1. Go to the [Google Play Console](https://play.google.com/apps/publish/) and log in.
2. Go to *Google Play Console → Manage developer accounts → Choose developer account → Setup → API Access* and
choose your Google Play Console Developer project from step 2 where you have created your Service Account.
:::note
If you are not able to see **Google Play Console Developer** project in the list then please update the webpage.
If the issue persists, make sure that your Google Play developer account (email) is the owner of the Google Cloud
project. You can read more [here](https://developers.google.com/android-publisher/getting_started).
:::
3. At the bottom there will be a list of service accounts that are available in this Google Cloud project.
Select the one from which the JSON was sent.
:::note Press **Refresh** if you are not able to see your Service Account.
:::
4. Press **View Play Console** permissions. In the **App Permissions** section select the necessary applications
where in-app events will be used.
5. Go to **Account Permissions** section and select all **Financial Data** permissions:
- View financial data
- Manage Orders and subscriptions
-------------
### Step 5. Contact Us
After all completed steps contact our support team via email [support@appodeal.com](mailto:support@appodeal.com)
or a live chat with the following information:
1. Service account JSON file. (Android only)
2. Purchases implementation logic in your app (when and where you call validate method and validate purchases)
3. Send us the purchase testing access through Google Developer console to email
[support@appodeal.com](mailto:support@appodeal.com). (Android only)
4. Your apk in zip for testing. (Android only)
5. Allow us to test purchases in your app and send us the testflight to email
[support@appodeal.com](mailto:support@appodeal.com). (iOS only)
-------------
### Step 6. Testing
After you have contacted our Support Team and provided all the required information you can test your app to make
sure purchases are validated.
1. Please go to your *App Settings → Attribution Settings* and change Adjust Environment from `Production` to `Sandbox`
to be able to test validation and don't forget to press **Save** at the end of the page.
2. Connect your device to your computer with the opened console (Android Studio logcat or iOS Console) and tag
logs by **purchase**.
3. Now you can open your App and make a test purchase, if you can see **Valid Purchase** in the console, then
validation went successfully.
4. If validation has failed, then please recheck all the steps above.
5. After testing, change your Adjust Environment to `Production` in *App Settings → Attribution Settings*.
-------------
---
## Launching a tROAS campaign in Google Ads(3)
**tROAS (target Return On Ad Spend )** is Google's smart bidding strategy that uses auction-time bidding to reach
your specified value.
Target ROAS regulates bids to maximize the value of your conversions.
By simply integrating Appodeal SDK in your app, you will be able to send ad revenue data to Firebase and launch a
tROAS campaign in Google Ads.
:::info Minimum Requirements:
- Appodeal SDK 3.0.0+ with Firebase (included by default).
:::
-------------
## Step 1. Integrate Firebase
Complete all the steps from our [Firebase integration guide](../services/firebase).
-------------
## Step 2. Set Up Your Firebase Account
:::info Make sure you have admin access to your Firebase and Google Ads accounts.
:::
1. You need to link your Firebase project with your Google Adwords account. In order to do so, please go to your
*Firebase project → Project settings → Integrations → Google Ads → Link* and choose your Google Ads account.
2. You can use Google Analytics to measure ad revenue generated from displaying ads.
To measure ad revenue, we are logging the **custom_ad_impression** event whenever your user sees an advertisement
in your app.
In Analytics, your most important events are called conversions, and in order to be able to import them to your
Google Adwords in the next step, account you need to mark the **custom_ad_impression** event as a conversion
by going to your *Firebase project → Analytics → Events*.
:::info Event reports are available within 24 hours on the Firebase side
:::
-------------
## Step 3. Set Up Your Google Adwords Account
Go to your *Google Adwords account → Tools and Settings → Conversions → New Conversions → Import → Google Analytics
4 properties → App* and import `first_open` and `custom_ad_impression` conversions.
Now you have set up everything and it is time to create a campaign on Google Ads side.
Check [this guide](https://support.google.com/google-ads/answer/6268637?hl=en) to learn more about tROAS bidding.
-------------
---
## Logging(Advanced)
SDK logging allows you to check SDK integration and activity, including information about waterfalls with ad units,
ads requests, loading, and some other. We recommend always enabling logs and using the debug logs to get full SDK
information.
Enable logging using the code below before SDK initialization:
```csharp
Appodeal.SetLogLevel(AppodealLogLevel.Verbose);
```
Available parameters:
- `AppodealLogLevel.None` - logs off;
- `AppodealLogLevel.Debug` - debug messages;
- `AppodealLogLevel.Verbose` - all SDK and ad network messages.
```csharp
Appodeal.setLogLevel(Appodeal.LogLevel.Verbose);
```
Available parameters:
- `Appodeal.LogLevel.None` - logs off;
- `Appodeal.LogLevel.Debug` - debug messages;
- `Appodeal.LogLevel.Verbose` - all SDK and ad network messages.
:::info SDK logs will not appear in the Unity debugger.
:::
-------------
## Android
Connect a device with the app installed, open the Android Studio Logcat console, run the app and check SDK logs
by the `Appodeal` tag.
For more information about the console please visit
[Debugging with Android Studio](https://developer.android.com/studio/debug/am-logcat).
-------------
## iOS
Connect a device with the app installed, open the Xcode console, run the app, and check SDK logs by the
`Appodeal` tag.
For more information about the console, please visit
[Debugging with Xcode](https://developer.apple.com/documentation/os/logging/viewing_log_messages).
Here is an example of Appodeal IOS SDK for interstitial ad type. Please note, logs can be different if you use
another ad type or a different SDK configuration.
```csharp showLineNumbers
// Information about sdk initialization
[Appodeal 3.0.2] [info] [application] SDK was running on simulator
[Appodeal 3.0.2] [debug] [services] Initialize Stack Analytics service with parameters:
[Appodeal 3.0.2] [debug] [services] Complete services manager initialization
// Default configuration for banner
[Appodeal 3.0.2] [debug] [impression] Banner APDAutolayoutBannerView 140615982580656 size: {320, 50} change size to {320, 50}
[Appodeal 3.0.2] [warning] [impression] Banner APDAutolayoutBannerView 140615982580656 size: {320, 50} unable to use smart sizing!
// Networks adapters and and their versions
[Appodeal 3.0.2] [info] [api] MRAID integration via SDK of version 2.0.4
[Appodeal 3.0.2] [info] [api] Crosspromo & Direct Offers integration via SDK of version 3.0.2
[Appodeal 3.0.2] [info] [api] IronSource integration via SDK of version 7.2.6
[Appodeal 3.0.2] [info] [api] AdColony integration via SDK of version 4.9.0.0
[Appodeal 3.0.2] [info] [api] NAST integration via SDK of version 2.0.4
[Appodeal 3.0.2] [info] [api] AppLovin integration via SDK of version 11.6.1
[Appodeal 3.0.2] [info] [api] Vungle Ads integration via SDK of version 6.12.1
[Appodeal 3.0.2] [info] [api] BidMachine integration via SDK of version 2.0.0.5
[Appodeal 3.0.2] [info] [api] Unity Ads integration via SDK of version 4.5.0
[Appodeal 3.0.2] [info] [api] Meta Audience Network integration via SDK of version 6.12.0
[Appodeal 3.0.2] [info] [api] VAST integration via SDK of version 2.0.4
[Appodeal 3.0.2] [info] [api] MyTarget integration via SDK of version 5.17.2
[Appodeal 3.0.2] [info] [api] A4G integration via SDK of version afma-sdk-i-v9.14.0
[Appodeal 3.0.2] [info] [api] Yandex Mobile Ads integration via SDK of version 5.2.1/4.4.0
[Appodeal 3.0.2] [info] [api] Notsy integration via SDK of version afma-sdk-i-v9.14.0
[Appodeal 3.0.2] [info] [api] Google Mobile Ads integration via SDK of version afma-sdk-i-v9.14.0
// Mediation start
[Appodeal 3.0.2] [debug] [mediation] Starting APDInterstitialAdModule
[Appodeal 3.0.2] [debug] [mediation] Starting APDAdQueueManager for "Interstitial Ad" ad request
[Appodeal 3.0.2] [info] [mediation] Trying to fetch waterfall
[Appodeal 3.0.2] [info] [mediation] Mediation start for impression: C73CF384-3844-4D18-84EC-BEB33EDC923A
// Requesting process starts from the most expensive ad unit to the cheapest.
// SDK makes a request and, if network can’t return the ad (with result: No fill), SDK will continue to do requests until it gets an ad.(with result: Fill)
// Information and result of each requested ad unit you can find in the logs.
// Rewarded video ad unit from admob with eCPM 1000.0 is not loaded due to no fill from the network,
// SDK will continue to do requests, the next ad unit is admob with eCPM 70.0 etc. :
[Appodeal 3.0.2] [info] [mediation] Start to load Rewarded Video admob wo pricefloor admob_rewarded_video_1000.0 eCPM = 1000.000000
[Appodeal 3.0.2] [info] [mediation] Complete loading Rewarded Video admob wo pricefloor admob_rewarded_video_1000.0 eCPM = 1000.000000 with result: No fill
[Appodeal 3.0.2] [info] [mediation] Start to load Rewarded Video admob pricefloor admob_rewarded_video_70.0 eCPM = 70.000000
[Appodeal 3.0.2] [info] [mediation] Complete loading Rewarded Video admob pricefloor admob_rewarded_video_70.0 eCPM = 70.000000 with result: No fill
// The ad is loaded (fill):
[Appodeal 3.0.2] [info] [mediation] Start to load Interstitial Ad applovin wo pricefloor applovin_interstitial_0.7 eCPM = 0.700000
[Appodeal 3.0.2] [info] [mediation] Complete loading Interstitial Ad applovin wo pricefloor applovin_interstitial_0.7 eCPM = 0.700000 with result: Fill
[Appodeal 3.0.2] [debug] [mediation] Trying to proceed ad unit:Interstitial Ad backfill wo pricefloor mraid_interstitial eCPM = 0.001000
[Appodeal 3.0.2] [debug] [impression] Skip ad unit cause SDK already contains ad with eCPM: 0.70 higher than unit: Interstitial Ad backfill wo pricefloor mraid_interstitial eCPM = 0.001000
[Appodeal 3.0.2] [debug] [mediation] Break mediation
[Appodeal 3.0.2] [info] [mediation] Complete loading Interstitial Ad backfill wo pricefloor mraid_interstitial eCPM = 0.001000 with result: Break AdUnit
[Appodeal 3.0.2] [debug] [mediation] Mediation completed
[Appodeal 3.0.2] [info] [mediation] Mediation complete for impression: C73CF384-3844-4D18-84EC-BEB33EDC923A
// The ad is shown, finished, clicked and closed:
[Appodeal 3.0.2] [debug] [api] [Callback] [Interstitial] Did appear
[Appodeal 3.0.2] [debug] [impression] Impression succesfully tracked
[Appodeal 3.0.2] [debug] [api] [Callback] [Interstitial] Did click
[Appodeal 3.0.2] [debug] [impression] Click succesfully tracked
[Appodeal 3.0.2] [info] [impression] Track viewability finish for item: Interstitial Ad applovin wo pricefloor applovin_interstitial_0.7 eCPM = 0.700000
[Appodeal 3.0.2] [debug] [api] [Callback] [Interstitial] Did disappear
// By default auto cache is enabled, sdk starts to request ad units in the waterfall after the ad disappeared from the screen:
[Appodeal 3.0.2] [info] [impression] Prepare impression storage for reuse
[Appodeal 3.0.2] [info] [mediation] Mediation start for impression: D892B114-DDA5-4B31-BEF2-46DAD770C9B0
[Appodeal 3.0.2] [info] [mediation] Original Interstitial Ad waterfall
[Appodeal 3.0.2] [info] [mediation] Start to load Interstitial Ad admob precache wo pricefloor admob_interstitial_0.57 eCPM = 0.570000
```
-------------
---
## Run Callbacks in Main Unity Thread
Callbacks in Appodeal plugin (only the Manual version!!!) are executed in the main Android or iOS threads
(not in the main Unity thread).
What does it mean for you? It’s not recommended to perform any UI changes (change colours, positions, sizes, texts
and so on) directly in our callback functions.
It's important to understand that it's not possible to receive callbacks in main thread while fullscreen advertising
is shown. This is because Unity pauses main thread if game scene is out of screen. That’s why your script will receive
some callbacks (like `onInterstitialShown` or `onRewardedVideoFinished`) in main thread only after closing fullscreen
advertising. But the same callbacks work without such delays in Android and iOS main threads.
So, how to react on Appodeal events to prevent multithreading problems? The simplest way is to use flags and `Update`
method of `MonoBehaviour` class:
```csharp showLineNumbers
public class SomeClass : MonoBehaviour, IRewardedVideoAdListener
{
bool videoFinished = false;
double rewardAmount;
string rewardName;
public void onRewardedVideoFinished(double amount, string name)
{
rewardAmount = amount;
rewardName = name;
// It's important to set flag to true only after all required parameters
videoFinished = true;
}
// Update method always performs in the main Unity thread
void Update()
{
if(videoFinished)
{
// Don't forget to set flag to false
videoFinished = false;
// Do something with rewardAmount and rewardName
}
}
}
```
-------------
Other, maybe more comfortable way is to use
[UnityMainThreadDispatcher](https://github.com/PimDeWitte/UnityMainThreadDispatcher). To use it:
1. Download script and prefab.
2. Import downloaded files to your project.
3. Add `UnityMainThreadDispatcher.prefab` to your scene (or to all scenes, where you want to make UI changes after
Appodeal callbacks).
4. Use `UnityMainThreadDispatcher.Instance().Enqueue()` method to perform changes:
```csharp showLineNumbers
public void onRewardedVideoFinished(double amount, string name)
{
UnityMainThreadDispatcher.Instance().Enqueue(()=> {
Debug.Log($"Appodeal. Video Finished: {amount} {name}")
});
}
```
-------------
And finally, the official way to send message to Unity Main thread is `UnitySendMessage`. It’s platform dependent,
so it’s required to make changes in Android native code and iOS native code.
You can find more information in the official unity documentation for
[Android](https://docs.unity3d.com/Manual/AndroidJARPlugins.html) and
[iOS](https://docs.unity3d.com/Manual/PluginsForIOS.html).
-------------
---
## Event Subscription Options
:::note Minimum Requirements:
- Appodeal SDK 3.0.1+ UPM distribution
:::
:::note
To further respond to your ad's behavior and track different Appodeal SDK events, you can either use our standard
callbacks implementation
([Interstitial](../ad-types/interstitial?distribution=manual#callbacks),
[Rewarded Video](../ad-types/rewarded-video?distribution=manual#callbacks),
[Banner](../ad-types/banner?distribution=manual#callbacks),
[MREC](../ad-types/mrec?distribution=manual#callbacks),
[Consent Manager](../data-protection/gdpr-and-ccpa?distribution=manual#handling-presentation-callbacks))
or subscribe only to the necessary ones.
Compared to standard callback implementation, our new logic is simpler and more user-friendly.
You can use one of the three options below to subscribe to the desired event:
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.RewardedVideo.OnFinished += (sender, args) => { };
}
```
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.RewardedVideo.OnFinished += (sender, args) => OnRewardedVideoFinished(args.Amount, args.Currency);
}
private void OnRewardedVideoFinished(double amount, string currency) { }
```
```csharp showLineNumbers
public void SomeMethod()
{
AppodealCallbacks.RewardedVideo.OnFinished += OnRewardedVideoFinished;
}
private void OnRewardedVideoFinished(object sender, RewardedVideoFinishedEventArgs e) { }
```
:::
-------------
---
## Segments & Placements
## Segments
Segments are used to track statistics for various user categories and manage ads for this categories. A segment is
a fraction of audience outlined based on certain parameters: e.g. gender, age or any other parameters known to the
app and passed to Appodeal SDK. Additional ad management settings can be applied to each segment. Read more on
segments in our [FAQ](/advanced/segments).
Once user segments have been created, they can then be analyzed and used to configure ads.
To create a new segment go [here](https://app.appodeal.com/v3/segments).
:::info
If you have no segments, all users will be assigned to default segment.
If you have multiple segments, their order is important. Only the first segment related to the given user will apply.
All of the rest will be ignored.
:::
-------------
### Manual Filters
Manual Filters allow to group users by any available metric. E.g. you know the sources that directed users to your
app and you want to track the statistics for such sources — create a segment for each source and mark each user
with the source they came from.
To create such a segment, you have to set its name and value:
```csharp showLineNumbers
Appodeal.SetCustomFilter("KEY_STRING", "SOME_VALUE");
Appodeal.SetCustomFilter("KEY_BOOL", true);
Appodeal.SetCustomFilter("KEY_INT", 42);
Appodeal.ResetCustomFilter("KEY_STRING");
```
```csharp showLineNumbers
Appodeal.setCustomFilter("KEY_STRING", "SOME_VALUE");
Appodeal.setCustomFilter("KEY_BOOL", true);
Appodeal.setCustomFilter("KEY_INT", 42);
Appodeal.resetCustomFilter("KEY_STRING");
```
Value can be boolean, numeric or string-based.
------------------
### Bought In-Apps and In-Apps Amount Filters
**Bought In-Apps** allows to group users by the fact of purchasing in-apps. This will help you adjust the ads for
such users or turn them off if needed.
**In-Apps Amount** filter allows you to group users who’ve made a particular amount of in-app purchases.
Please submit the purchase info via Appodeal SDK to make these settings work correctly.
```csharp
Appodeal.TrackInAppPurchase(5.0,"USD");
```
```csharp
Appodeal.trackInAppPurchase(5.0,"USD");
```
-------------
If you have no segments, all users will be assigned to default segment.
If you have multiple segments, their order is important. Only the first segment related to the given user will apply. All of the rest will be ignored.
## Placements
Appodeal SDK allows you to tag each impression with different placement. Read more on placements in our
[FAQ](/advanced/placements).
To show an ad with placement, you have to call show method like this:
```csharp
Appodeal.Show(adType, "placementName");
```
```csharp
Appodeal.show(adType, "placementName");
```
To check if an impression is available for a given placement, use:
```csharp showLineNumbers
if(Appodeal.CanShow(adType, "placementName")){
Appodeal.Show(adType, "placementName");
}
```
```csharp showLineNumbers
if(Appodeal.canShow(adType, "placementName")){
Appodeal.show(adType, "placementName");
}
```
You can configure your impression logic for each placement.
If you have no placements or call `Appodeal.Show()` with placement that does not exist, the impression will be
tagged with `default` placement with corresponding settings applied.
:::caution Important!
Placement settings affect **ONLY** ad presentation, not loading or caching.
:::
-------------
---
## Self-Hosted Bidon(3)
Configuring and retrieving the Bidon endpoint.
:::info
Bidon documentation can be found [here](https://docs.bidon.org/).
:::
### Set Bidon Endpoint
To set a custom Bidon endpoint, use the following method:
```csharp
Appodeal.SetBidonEndpoint("https://example.com/api");
```
:::info Should be called before the SDK initialization.
:::
-------------
### Get Bidon Endpoint
To retrieve the currently set Bidon endpoint, use the following method:
```csharp
Appodeal.GetBidonEndpoint();
```
-------------
---
## Testing(3)
After adding a new app to Appodeal and integrating the SDK, we recommend testing your app. Here are the tips for
successful testing.
## Integration Review
### Step 1. Prepare Settings On Appodeal Side
#### Check Mediation Settings
Go to Application Settings → Mediation Settings → Line Items.
Choose the ad type you are interested in and check the network
connection.
In the Line Items section, you can see the rules for automatically
connecting ad networks. Once you fulfill all the requirements, networks
will be connected automatically using the default Appodeal account.
**Example:**
For new applications, a few networks will be connected by default if the Appodeal server receives a request for a
certain ad type.
If you see `This network will be activated by ad request.`
Try to [request real ads](#check-sdk-integration-with-real-ads) to activate this network using the default Appodeal
account.
:::info
Make sure you have **at least 2-3 enabled** networks. If the requirements for automatic network connection are
not fulfilled, link a personal account using
[Networks Setup](/networks-setup/introduction) to have more networks connected.
:::
Make sure the ad units are enabled for the connected networks:
#### Check Priorities (Waterfall Configuration)
Go to Application settings → Mediation Settings → Priorities, and
choose the ad type.
By default, only default priority configuration is enabled for the waterfall, where all ad units from connected
networks are placed.
Make sure line items have been added to your current configuration.
If not, add them to the configuration by dragging and dropping ad units
from the Unused Line Items list on the left to Automatic Priority.
### Step 2. Test Your SDK Integration
#### Check SDK Integration With Test Ads
:::info
Test mode ads have a 100% fill rate, they load almost instantly compared to real ads, which can take some time
to load (0-30 seconds depending on the ad type).
:::
1. [Enable Test Mode](#enable-test-mode)
2. [Enable SDK Logging](#enable-logging)
3. Make sure that all necessary adapters have been integrated into the app.
4. Run the app and go to all placements where you added ads. Make sure they are loaded and shown successfully.
5. Open the logs tab and check Appodeal SDK logs. For more information, look through the [SDK logging](./logging)
:::info
Requests for test ads are not counted as real requests, however, Appodeal needs at least one real request for
automatically activating networks for a certain ad type.
:::
#### Check SDK Integration With Real Ads
We recommend testing apps using test mode to ensure proper performance with real ads. However, it's necessary to
make sure SDK integration is correct and all networks are ready to use before publishing.
1. Disable test mode by commenting out the method you used to enable it.
2. Check that Appodeal SDK [logging](#enable-logging) is enabled.
3. Make sure that all necessary adapters for the networks you plan to use were integrated.
4. Open your application and initialize SDK to make a request for activating the ad networks. You can see all the
activity of our SDK in the logs under the `Appodeal` tag.
5. When network setup is ready, run the app again and open the logs console. Make sure there are no errors in the logs.
Use [SDK logging](./logging) to analyze Appodeal logs. Go through all placements where you added ads. Make sure
they are loaded and shown successfully with no exceptions and errors.
:::info
If your app is not published in one of the supported app stores (Google Play, App Store, Amazon), the number of
impressions for live ads is restricted to [two thousand](/faq-and-troubleshooting/faq/ad-mediation/traffic-limit).
:::
-------------
## Useful SDK Methods
### Enable Test Mode
Using test mode allows you to get our test ad creatives with 100% fillrate.
```csharp
Appodeal.SetTesting(true);
```
```csharp
Appodeal.setTesting(true);
```
:::info Should be called before the SDK initialization.
:::
-------------
### Enable Logging
To enable debug logging, use the code below:
```csharp
Appodeal.SetLogLevel(AppodealLogLevel.Verbose);
```
:::info Should be called before the SDK initialization.
:::
Logs will be written in the console using the `Appodeal` tag.
Available parameters:
- `AppodealLogLevel.None`- logs off;
- `AppodealLogLevel.Debug` - debug messages;
- `AppodealLogLevel.Verbose` - all SDK and ad network messages.
```csharp
Appodeal.setLogLevel(Appodeal.LogLevel.Verbose);
```
:::info Should be called before the SDK initialization.
:::
Logs will be written in the console using the `Appodeal` tag.
Available parameters:
- `Appodeal.LogLevel.None`- logs off;
- `Appodeal.LogLevel.Debug` - debug messages;
- `Appodeal.LogLevel.Verbose` - all SDK and ad network messages.
-------------
### Disable Networks
```csharp
Appodeal.DisableNetwork((string)network);
```
:::info Should be called before the SDK initialization.
:::
Use constants from `AppodealStack.Monetization.Common.AppodealNetworks` to choose necessary network.
```csharp
Appodeal.disableNetwork((string)network);
```
:::info Should be called before the SDK initialization.
:::
Use constants from `AppodealAds.Unity.Api.AppodealNetworks` to choose necessary network.
-------------
### Disable Networks For Specific Ad Types
To disable networks for the specific ad formats use the following method:
```csharp showLineNumbers
Appodeal.DisableNetwork((string)network, adTypes);
```
```csharp showLineNumbers
Appodeal.disableNetwork((string)network, adTypes);
```
:::info Important Should be called before the SDK initialization.
:::
-------------
### Test Adapters Integration
:::caution This method will take effect only on Android platform
:::
To check integration of the third-party networks, you need to start a test screen by calling the following method.
```csharp
Appodeal.ShowTestScreen();
```
```csharp
Appodeal.showTestScreen();
```
-------------
### Show Mediation Debugger
To show one of the predefined mediation debugger window providers, call the following method.
```csharp
Appodeal.ShowMediationDebugger(MediationDebuggerProvider.AppLovinSdk);
```
:::info
The method returns **true** if the chosen mediation debugger window was found and displayed, otherwise - **false**.
:::
The table below shows all available values for MediationDebuggerProvider enumeration.
| Debugger Provider | Description |
| --- | --- |
| **AppLovinSdk** | Displays the mediation debugger window from AppLovin Max. Requires Max SDK to be initialized. |
-------------
---
## User Data(3)
Our SDK provides user data tranfer for better ad targeting and higher eCPM. All parameters are optional.
## Set User Id
To assign an ID to a user, please call this method before Appodeal
initialization:
```csharp
Appodeal.SetUserId("YOUR_USER_ID");
```
```csharp
Appodeal.setUserId("YOUR_USER_ID");
```
-------------
:::caution
For data privacy and GDPR-compliance reasons, you may **NOT** use email address, phone number, real name or any
other personally identifiable information in the user ID you set with this call.
:::
## Custom Segment Matching
If the logic of your application allows specifying user's
characteristics, then you can pass specific parameters to the Appodeal
SDK. You can
use [Segments](/advanced/segments) in the future.
- For gender use `PredefinedKeys.UserGender`.
- For age use `PredefinedKeys.UserAge`.
```csharp showLineNumbers
Appodeal.SetCustomFilter("KEY_STRING", "SOME_VALUE");
Appodeal.SetCustomFilter("KEY_BOOL", true);
Appodeal.SetCustomFilter(PredefinedKeys.UserAge, 42);
Appodeal.ResetCustomFilter("KEY_STRING");
```
- For gender use `UserSettings.USER_GENDER`.
- For age use `UserSettings.USER_AGE`.
```csharp showLineNumbers
Appodeal.setCustomFilter("KEY_STRING", "SOME_VALUE");
Appodeal.setCustomFilter("KEY_BOOL", true);
Appodeal.setCustomFilter(UserSettings.USER_AGE, 42);
Appodeal.resetCustomFilter("KEY_STRING");
```
-------------
## Location
The SDK reads the device location only if your app has already obtained the OS location permission from the user. The SDK **does not request the location permission itself** — your app is responsible for requesting authorization.
**To opt out of location collection, do not request/declare the location permission** — on iOS omit the `NSLocationWhenInUseUsageDescription` key, on Android remove `ACCESS_COARSE_LOCATION` / `ACCESS_FINE_LOCATION` from the manifest. If the permission is missing, no location is collected.
Declare the collected location in your App Privacy Details ([App Store](/ios/data-protection/app-privacy-details) / [Google Play](/android/data-protection/app-privacy-details)).
-------------
## Send Extra Data
You can send key-value data to Appodeal.
```csharp showLineNumbers
Appodeal.SetExtraData("KEY_STRING", "SOME_VALUE");
Appodeal.SetExtraData("KEY_BOOL", true);
Appodeal.SetExtraData("KEY_INT", 42);
Appodeal.ResetExtraData("KEY_STRING");
```
```csharp showLineNumbers
Appodeal.setExtraData("KEY_STRING", "SOME_VALUE");
Appodeal.setExtraData("KEY_BOOL", true);
Appodeal.setExtraData("KEY_INT", 42);
Appodeal.resetExtraData("KEY_STRING");
```
To send the device identifier from a mobile attribution service and match it with the Appodeal user id, use
`attribution_id` as a key and a unique identifier from your attribution service as a value and if you use this
method for attribution, call it **before Appodeal SDK initialization.**
-------------
---
## Appodeal vs. own network accounts
There are two options available for connecting ad networks accounts: using Appodeal default account or your personal account.
Below you can find the differences between the options.
## Appodeal Default Account
Appodeal default accounts are supported for all the ad networks except [Admob](/networks-setup/ad-networks/network-connection/admob),
[Amazon](/networks-setup/amazon),[Meta Audience Network](/networks-setup/meta-audience), [Yandex](/networks-setup/yandex).
For the given networks you will need to connect your own account.
### Benefits
- **Automatic setup for the best performance.**
Optimal ad units configuration will be created automatically based on
historical data and expertise to provide the best performance.
- **No need to set up accounts on most networks yourself.**
You won't need to register all ad networks accounts and set up each of
them manually. All set up will be added automatically once the network
is connected.
- **Minimum manual work.**
You can save a lot of the time, since almost all settings will be
handled by Appodeal. You will only need to connect the networks, which
require a personal account (Admob, Meta Audience Network, Yandex).
- **Support for instant withdrawal of funds for most networks directly
from the Appodeal account.**
All ad networks revenue comes from the Appodeal default accounts will be
added on your Appodeal account balance and will be kept in one place.
You can withdraw all the income instantly or receive payments on
NET45 basis. Find more about payments [here](/faq-and-troubleshooting/faq/payout/methods-of-payments).
### Limitations
- **It is not possible to change ad units configuration yourself.**
Configurations will be set up automatically and there will be no access
to add or edit ad units, manage the settings on the networks side, set
up different price floors.
- **Specific requirements for connecting ad networks.**
Every ad network has its own requirements for automatic connection, e.g.
add app store link, impressions threshold, etc. and can be found in Line
Items. The restrictions applies by ad networks or Appodeal, if the setup
requires given amount of traffic for better performance results. Ad
network will be connected automatically after the app reaches the
requirements for connecting.
## Personal Ad Network Account
Personal accounts are supported for all ad networks. For [Admob](/networks-setup/ad-networks/network-connection/admob),
[Meta Audience Network](/networks-setup/meta-audience), [Yandex](/networks-setup/yandex) you can only connect a personal
account. How to connect own account for different networks you can find
in our Networks Setup guides.
### Benefits
- **Possibility to fully manage ad units and waterfalls.**
You will be able to fully manage settings on ad networks side and set up
ad units configuration or edit it based on your needs.
- **Conduct waterfall A / B tests.**
You will be able to try different ad units and waterfall configurations
and choose the one suits you the best.
- **Connect any network as needed.**
Without waiting until app meets the requirements for automatic
connection.
### Limitations
- **You have to withdraw funds from different ad networks**
When you connect your account to Appodeal, we do not have the full
access to it. We can only show the stats, not withdraw the money from
your account. That's why these funds are not added to your Appodeal
account balance. The revenue will be distributed between different ad
networks, which have different withdrawal rules and minimum withdrawal
amount. It complicates the process and increases the period of
withdrawal.
- **Complicated setup**
Setup can be time-consuming, not recommended for beginners and if you
don't have enough resources.
---
## Ad Networks
:::info
On the Appodeal side, you can either use **your accounts** for the ad
networks or you can use our **Appodeal default accounts**, but for some
ad networks, you can use only your accounts.
- **Appodeal Default Accounts:** To use Appodeal default accounts for the
ad networks, you need to full fill the requirements, which you can
find in your App Settings -\> Mediation Settings -\> Ad Units in
your Appodeal account.
- **Your Ad Network Accounts:** You can read more on how to connect
your ad network accounts below in our
**Networks Setup** guide for each ad network.
:::
**Mediation Ad Networks** settings allow you to manage linked ad network
accounts to be used within applications.
You can independently link your accounts and subsequently use them in
applications. For most networks, Appodeal accounts are used by default,
but you can always add your own. After linking, you can enable them in
your applications. Related income reports will be aggregated for you in
the Appodeal dashboard.
## Mediation Ad Networks
To find this tool navigate to the **Mediation Setup \> Ad
Networks tab**.
This tool is used to create and manage your ad network accounts. The
Admob account can be set up Manually. For other ad network
accounts, there are just a few simple fields to fill out. These fields
can be different from one ad network to another, depending on the set of
data points each ad network requires. You can set up any of the ad
network accounts as a default account, and we will use it for all the
new apps.
This tool is used to create and manage your ad network accounts.
Most ad networks connect automatically. But if you want, you can use
your own ad networks. [read more](ad-networks/appodeal-vs-own-network-accounts.mdx)
There are just a few simple fields to fill out. These fields
can be different from one ad network to another, depending on
the set of data points each ad network requires.
Some networks, such as AdMob and Meta, only support your own accounts.
To connect AdMob, use the following [guide](network-connection/admob.mdx)
To connect Meta Audience, use the following [guide](network-connection/meta-audience.mdx)
You can set up any of the ad network accounts as a default account,
and we will use it for all the new apps.
---
## How do I link my InMobi account?
:::warning
Inmobi is not available for Appodeal SDK since **v2.11.0** (Android), **v2.10.3** (iOS).
:::
You need the following information to link your account: **Account id**, **API Key**, **Placement ID**, **Site ID**.
**Account id**: When you login to your account, go to **Monetize**. Next to your registered email there is an arrow that you can click on. In the appearing menu Account id information comes first.
**API Key**: Click on the arrow next to your registered email and choose **Account Settings**. Click on **API Key** and then choose **Generate API Key**.
**Placement ID** and **Site ID**: Click on the app and choose **Placements**. In the pop-up window **Placement ID** and **Site ID** are next to each other in the upper part of the window.
---
## How do I link my Mintegral account?
:::warning
Mintegral is not available for Appodeal SDK since **v2.6.2** (Android), **v2.10.3** (iOS).
:::
You need the following information to link your account: **Skey**, **Secret**, **App Key**, **App ID** and **Ad Unit ID**.
**Skey** and **Secret**: On your Dashboard click on **Account** and choose **Company Info**. Skey and Secret are under **Reporting API**.
**App Key**, **App ID** and **Ad Unit ID**: On your Dashboard click on **APP Setting**. App Key is located in the upper part of the page in brackets. App ID and Ad Unit ID are located one after another in the main body of the page.
---
## How do I link my StartApp account?
:::warning
StartApp is not available for Appodeal SDK since **v2.11.0** (Android), **v2.10.3** (iOS).
:::
To connect your own StartApp account to Appodeal, follow these simple steps.
## Step 1: Create and prepare the network's account
First, you need to find your **account id**. It is located in the right upper corner of the [home page](https://portal.startapp.com/#/pub/reports/analytics).
Next, you should get your **partner id** and **token**. Currently, there is no public access to these fields, thus you need to contact Startapp [support](https://support.startapp.com/hc/en-us) to obtain them. Below, you can see an example of the request.
Please, be patient as this process can take some time. Usually, getting a response may take several days.
## Step 2: Add keys to the Appodeal dashboard
At this point, you should already have all the necessary keys described in the previous step. Now you need to add their values on the Appodeal side to finish linking your account. Go to the **Network Accounts** [page](https://app.appodeal.com/apps/linked_networks), find Startapp in the list of ad networks and click on the **Link your account** button.
Fill the following information in your **email** (associated with Startapp): **dev id**, **account id**, and **token.** Hit the **Create** button.
:::note
Dev id = partner id.
:::
## Step 3: Create ad units on the StartApp side
:::note
There is no such entity as ad units (or line items) with Startapp, thus it is only possible to create an application there.
:::
First, head to the **My Apps** [page](https://portal.startapp.com/#/pub/applications). Create a new app by entering the **App URL** and pressing the **Add App** button (you can use an existing one if you created it earlier).
Copy **App ID** value of the app you want to use with Appodeal.
## Step 4: Create ad units on the Appodeal side
Open **Mediation settings** of the same app in the [Appodeal](https://app.appodeal.com/apps) dashboard. In the **Line items** tab of **Mediation settings** find the Startapp ad network. Change an account for this network from Appodeal account to the one you created earlier.
Enter **app id** in the field **Startapp App ID** and press the **Save** button.
Then, click on the **Add line item** button and set it up in the same way as on the screenshot below.
Make sure that the newly created line item is marked as enabled (the toggle is on "ON" state).
Now you're all set!
---
## AdMob Sync
Appodeal yields optimal results in cooperation with AdMob.
Use our **AdMob Sync extension** or **AdMob Sync application** to link them.
The AdMob Sync application will allow Appodeal
to access your AdMob reports over API, and will create new ad units on
AdMob and submit them to Appodeal. You can check the code of the
application [here](https://github.com/appodeal/admob-sync-app).
AdMob Sync app is opensource under MIT license.
Download **AdMob Sync Extension** and **AdMob Sync App** using the button below:
Check our video guide on how to perform synchronization:
## AdMob Sync Extension (Recommended)
### Install
1\. Follow [this link](https://amsa-updates.appodeal.com/) to download the extension.
2\. Go to Chrome browser → Settings → Extensions → turn on **Developer mode** in the top right corner.
3\. Press **load unpacked** and choose the downloaded unpacked extension from step 1.
### Synchronization
1\. To prepare your apps for synchronizing with the AdMob account go to the App settings → Mediation Settings → Line Items → choose your AdMob account for each ad type in your app.
2\. Now you can perform synchronization by pressing the extensions icon → Appodeal extension → Sync
3\. Follow the instructions and log in to your AdMob account, when the synchronization starts you will see the window below, do not close it and wait till it finishes.
## AdMob Sync App
## Install
1\. Follow [this link](https://amsa-updates.appodeal.com/download) to download the application.
We support Windows, Mac OS and Linux.(Check our [FAQ](#admobsyncfaq) for more information).
2\. Go to your Download folder and install AdMob Sync application.
## Sign in
Start the application.
Use your Appodeal account credentials to sign in. If you don’t have an
Appodeal account, visit www.appodeal.com to register.
:::info
If you have used Google Sign-in option to sign in, then you need to create a **password** for your Appodeal
account by going to Appodeal Account → Account Settings and confirm it via email.
:::
Use the menu bar icon for quick access to the application. Click it to
open the drop-down menu.
:::info Please note
To be able to run sync in the application, you need to
sign in.
:::
## Link with AdMob
Let's consider two cases - when your AdMob accounts are already
registered with Appodeal and when they are not.
Use the Accounts tab to manage AdMob accounts.
1. Your AdMob account is already registered with Appodeal.
AdMob accounts registered with Appodeal will be automatically added to
the application.
You need to sign in to each account to activate it. Use your AdMob
account email and password.
2. You want to add a new AdMob account.
To add a new AdMob account, press the + button, you will be redirected
to the [link](https://www.appodeal.com/apps/linked_networks#AdmobAccount). Press Link your account and follow instructions.
To add several AdMob accounts, repeat the steps above for each new
account.
:::info
To delete an AdMob account from the application, visit your Dashboard at
www.appodeal.com , as you have to delete all linked applications first.
:::
:::info
One AdMob account can be registered with several Appodeal accounts.
:::
## Sync set up
The sync is performed automatically:
- when the application is started;
- 24 hours after the last sync (regardless of the synchronization
result).
Also, you can start it manually for each AdMob account.
## Read reports
The report summary line looks as follows:
The state of the sync can be one of those:
– all data was synced correctly.
– sync was interrupted, the data is incomplete.
- the sync failed or completed with errors.
The sync can be started:
- automatically, in accordance with the schedule( every 24 hours).
- manually.
- quantity of
applications that have been affected since the last sync - created,
changed, or renewed. Click the line to display the change details.
- timestamp of the sync.
- technical log information.
- allows to submit the log immediately to the support team in case of any issues.
## Update
There are two ways to check for updates.
1\. Click the Appodeal icon in the menu bar to open the drop-down menu
and select **Check for updates**.
2\. Open the **Settings** tab in the application and press **Check for updates**.
Set the check schedule of your own.
## Clean up
The application stores 100 last logs. Earlier logs will be erased
permanently.
Use **Clear All** button to permanently delete all account related
information. To be able use the application after this action, you will
have to sign in.
## Sign out
When signing out, all data related to the current session will be
removed.
## FAQ{#admobsyncfaq}
### How to run the AdMob Sync app on Linux
Please complete the steps below to convert the AdMob Sync app to be able to run it on Linux :
```
debtap admob-sync-setup.deb
pacman -u admob-sync-setup.pkg.tar.zst // package, which was generated from the debtap command above
```
### How to Sign in to your AdMob Account in case of an insecure browser issue in AdMob sync app
1\. Сhange your default browser to Mozilla, Edge, or Opera using the instruction for
[Windows](https://support.microsoft.com/en-us/windows/change-your-default-browser-in-windows-10-020c58c6-7d77-797a-b74e-8f07946c5db6)
and [Mac](https://support.apple.com/en-us/HT201607), please try different browsers and try **several times** to sign in.
2\. Try to use our [Admob Sync Extension](#admob-sync-extension) instead.
## Changelog
#### 0.1.51 (Oct 17, 2023)
- Added Admob Bidding support
#### 0.1.50 (Oct 17, 2023)
- Fixed errors during sync for Amazon apps
#### 0.1.49 (Oct 17, 2023)
- Minor fixes
#### 0.1.48 (Oct 13, 2023)
- Minor fixes
#### 0.1.47 (Oct 13, 2023)
- Minor fixes
#### 0.1.46 (Oct 13, 2023)
- Minor fixes
#### 0.1.45 (Oct 6, 2023)
- Updated web pack config
#### 0.1.44 (Oct 6, 2023)
- Minor fixes
#### 0.1.43 (Oct 6, 2023)
- Minor fixes
#### 0.1.42 (Jan 31, 2023)
- Updated Browser version
#### 0.1.41 (Sep 29, 2021)
- Deprecated auto setup on Google developer console prior to new reporting API
#### 0.1.40 (Sep 13, 2021)
- Fixed broken build after Electron version update
#### 0.1.39 (Sep 13, 2021)
- Electron updated to 13.3.0
- Use new API to create apps on AdMob
#### 0.1.38 (Dec 16, 2020)
- Updated URL to Appodeal's dashboard
#### 0.1.37 (Mar 12, 2020)
- Fixed syncing apps with long bundle id.
#### 0.1.36 (Jan 30, 2020)
- Simplified adding AdMob accounts. New AdMob accounts are added via
Appodeal's dashboard.
#### 0.1.35 (Nov 4, 2019)
- Display logs on developers console (for chrome extension) while sync
is running
#### 0.1.34 (Sept 24, 2019)
- Minor fixes
#### 0.1.33 (Sept 23, 2019)
- Minor fixes
#### 0.1.32 (Sept 19, 2019)
- Minor fixes
#### 0.1.31 (Sept 6, 2019)
- Fixed clear data issue for Win32 platform
#### 0.1.30 (Sept 2, 2019)
- Minor fixes
#### 0.1.29 (Aug 29, 2019)
- Minor fixes
#### 0.1.28 (Aug 26, 2019)
- Fixed issues at login step
#### 0.1.25 (Jul 24, 2019)
- Minor fixes
#### 0.1.24 (Jul 23, 2019)
- Support of 32-bit version for Win32 platform added
#### 0.1.23 (Jul 15, 2019)
- Only one app instance is allowed to run
#### 0.1.22 (Jun 24, 2019)
- License info is added
- Improved matching of adunits created by legacy chrome extension
#### 0.1.18 (Jun 6, 2019)
- Public release
---
## AdMob
Dynamic ad network for mobile apps & games with all kinds of ad types.
Highly recommended integration for those interested in increasing fill
rates on worldwide markets.
:::info Minimum requirements:
Make sure you haven't excluded AdMob from Appodeal SDK. It is included
by default.
:::
## Step 1. Prepare AdMob account{#step1}
Register your AdMob account with your email or
[create a new one](../../img/attachments/networks-setup/ad-networks/network-connection/admob-sync).
You will need to confirm your account and fill in the payment
information. Details can be found
[here](https://support.google.com/admob/answer/2772302?hl=en).
## Step 2. Link your AdMob Account with Appodeal{#step2}
1. Open [Mediation Ad Networks](https://app.appodeal.com/integrations/mediation_ad_networks)
page on Appodeal.
2. Click on the **AdMob**.
3. Select **Sign in with Google** or **Link your account**.
4. Follow Google authorization flow.
## Step 3. AdMob Sync{#step3}
To simplify the work of creating the applications on the AdMob side, we
have prepared an application for synchronization. The application will
complete all necessary steps for the correct operation of AdMob with
Appodeal.
You can learn more about AdMob Sync app [here](/networks-setup/ad-networks/network-connection/admob-sync).
1. Follow [this link](https://amsa-updates.appodeal.com/download)
to download the extention or the application. We support Windows,
Mac OS, and Linux.
2. Go to your Downloads folder and install the AdMob Sync extention/application.
:::info
To prepare your apps for synchronizing with the AdMob account, please
make sure that you have chosen the current AdMob account for each type
of ad in these apps.
:::
**Sync AdMob account:**
1. Run the extention/application.
2. Use your Appodeal account credentials to sign in.
:::info
If you have used Google Sign-in option to sign in, then you need to
create a **password** for your Appodeal account by going to Appodeal
Account → Account Settings and confirm it via email.
:::
3. Add your AdMob account. To add a new AdMob account, press the **+**
button and you will be redirected to the
[AdMob account page](https://www.appodeal.com/apps/linked_networks#AdmobAccount). Press **Link your account**
and follow instructions.
4. After you have made all settings in your Appodeal account, go to the
AdMob Sync app and press **Run Sync**.
## Step 4. Create Line Items For Your App Manually (Optional){#step4}
:::info
We recommend following Step 3 from this guide and letting our Admob sync
app create the ad units both on AdMob and Appodeal side to reach better
results. The [AdMob Sync](/networks-setup/ad-networks/network-connection/admob-sync) application will allow Appodeal to access your
AdMob reports over API and will create new ad units on AdMob and submit
them to Appodeal. You can check the code of the
application [here](https://github.com/appodeal/admob-sync-app). AdMob Sync app is open
source under MIT license.
:::
If you want to create your AdMob blocks manually, please follow the next
steps.
1. Go to your AdMob account → Select your App Settings and copy your
AdMob app ID part after ~ as shown below and save it for future
steps:
2. Go to the ad units section and create ad units for your app. After
successful creation, copy your ad unit ID and save it for future
steps.
3. Go to your Appodeal account → app settings → Mediation Settings →
Line Items → select your AdMob account for the needed ad types and
insert previously copied AdMob App ID in the corresponding field
below.
4. Then press Add Line Item button and insert your ad unit name from
AdMob in the Line Item Label field and the previously copied ad unit
ID in the Code field.
Repeat these steps for all of your ad units created on the AdMob
side.
## Step 5. Update the Project{#step5}
### Android{#step5-android}
Add **``** tag to the **AndroidManifest** file.
``` xml
```
### iOS{#step5-ios}
1. In the **info.plist** file add **GADApplicationIdentifier** key.
2. Add identifier's String value.
3. Copy the code below:
``` xml
GADApplicationIdentifierYOUR_ADMOB_APP_ID
```
## Step 6. Update App-ads.txt file{#step6}
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. After copying App-ads.txt from
[this page](https://www.appodeal.com/profile/app_ads_txt), you need to replace the existing
line under the comment "#Replace the Publisher ID below with your
AdMob Publisher ID" with your unique AdMob app-ads string or simply
replace the Publisher ID with yours from the top right corner(press
your account icon).
2. To get AdMob app-ads string, open your
[AdMob account](https://apps.admob.com/signup/admob-account) -\> View all apps -\> Click
App-ads.txt at the top part of the page and click the blue button
"How to set up app-ads.txt" , then copy the AdMob string and paste
it instead of the mentioned above string in the generated by
Appodeal file.
3. Save changes and wait at least 24 hours for ad networks to crawl and
verify your app-ads.txt file. Make sure you have added the
app-ads.txt file to the correct path as written in [this guide](https://support.google.com/admob/answer/9363762?hl=en).
Now you can monetize with AdMob through Appodeal.
---
## Amazon
Suitable for apps approved by Amazon ad network for banner and MREC ad
types.
:::info
For Amazon, you can use only your Amazon Account. You can read more
about Network Accounts [here](/networks-setup/appodeal-vs-own-network-accounts).
To connect your Amazon account to Appodeal, follow the steps below.
:::
:::info Before the start
- Make sure you use Appodeal SDK 2.10.3+/ Appodeal Unity plugin
2.14.5+.
- Make sure BidMachine is enabled in Mediation Settings.
- Make sure you have a BidMachine Adapter in your project. BidMachine
is included in Appodeal SDK by default, make sure you haven't
removed it from the project.
- Make sure you have an Amazon Adapter in your project (Only for iOS
platform).
:::
## Step 1. Prepare Amazon account
:::info
If you have already created an Amazon publisher services account, skip
this step.
:::
1. In order to create an Amazon account, please
submit [this form](https://aps.amazon.com/aps/contact-us/) on the Amazon side.
Once it is approved, Amazon will send the invitation link for account creation to your email.
:::info
Proceed with step 1 point 2 of the guide only after receiving the link from Amazon.
Ad monetization through Amazon is still in beta. Not all publishers and
apps can be approved by Amazon ad network.
:::
2. Follow [the link](https://www.amazon.com/ap/signin?openid.assoc_handle=dtb_portal_us&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.return_to=https%3A%2F%2Fams.amazon.com%2Fwebpublisher%2Fsessions%2Flogin%3Fu%3Dhttps%253A%252F%252Fams.amazon.com%252Fwebpublisher%253Fdistinct_id%253D180514cb3e1dd-004b02164e695f-133a645d-1aeaa0-180514cb3e2c6e)
and click **Create your Amazon account**.
3. Fill in all the fields and click **Create your Amazon account**.
After that, you should receive a one-time password (OTP) to your email.
Enter the OTP and click **Create your Amazon account.**
4. Reach out to support@appodeal.com
once your Amazon account has
been created. We will contact Amazon to provide you with access to S3
credentials (they are required to import reports).
## Step 2. Create a new Amazon app
1\. Go to the **Setup** tab and click **SET UP** under the **App &
Slot** section.
2\. Click **Add New App**.
3\. Insert **App Market URL**, click **Find App**, check the COPPA
agreement and click **Add App**.
Once you’ve added your app, you will receive the **App ID**. Your app
submission will need to be approved by the APS team before your
integration can go live. Please revisit this page to check your app
approval status. As your app is getting approved, you can continue with
your setup.
## Step 3. Add ad slots to the created app
Once you’ve set up your apps, you will configure your slots to generate
**SlotUUID**s. Slots (aka ad units) are the ad spaces you define in your
app.
1. Click **SETUP** under the **Actions** column to add a new ad slot.
2. Select **Other** as a **Monetization Service** and click **Add A
Slot**.
:::info
At the moment Appodeal supports only two ad types: banner and MREC.
:::
3. Choose **Media Type**, **Ad Size** and **Slot Name**. Use **Recommended Price
Points** by default and save the ad slot.
:::info
It is mandatory to specify device type for banner line items (phone or
tablet).
Phone banner - 320x50 ad size
Tablet banner - 720x90 ad size
MREC - 300x250 ad size
:::
4\. You can find the **App ID** and **Slot ID** (**UUID**) here. Save
them, you will need them later.
5. Download the **Price Points csv** files and provide them to support@appodeal.com. We will make the appropriate
settings for your apps on the Appodeal end.
## Step 4. Configure Amazon reporting
1\. Open a new custom report form and click **Create report**.
2\. Fill out the form as shown below and click **Save**.
3\. Get the report S3 location here. Save it, you will need it later.
4\. Go to **Account settings** → **S3 Credentials**.
5\. Get the **S3 Access Key** and **S3 Secret Access Key**. Save them,
you will need them later.
:::info
Each credential pair will expire every 90 days. When your “active
credentials” have less than 30 days remaining, your “next credentials”
will be available. Please start using your new credentials (i.e. next
credentials) prior to the expiration of your current active credentials.
:::
## Step 5. Link your Amazon account with Appodeal
1\. Go to [this page](https://www.appodeal.com/apps/linked_networks) and click **Amazon**.
2\. Click on the **Link your account** button.
3\. Fill in the following details (numbers in brackets refer to points
in the instructions):
- Your Amazon account email (1.1)
- Custom report S3 location (4.3)
- S3 Access Key & S3 Secret Access Key (4.5)
4\. Select the appropriate app
on [this page](https://www.appodeal.com/apps),
open **App Settings** and select **Mediation Settings**.
5\. Go to the desired ad type, choose the created Amazon account, click
on “**+Add Line item**” and fill in:
- Amazon key - your Amazon App ID (2.7)
- Line item Code - Amazon UUIDs (2.7)
## Step 6. Update app-ads.txt file
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
1\. Go to app-ads.txt Setup tab and click Update for the application.
2\. Copy the missing string and update your **app-ads.txt** file.
Now you can monetize with Amazon through Appodeal.
---
## AppLovin
Take your mobile monetization to the MAX with an unmatched scale.
:::info Minimum requirements:
Make sure you haven't excluded AppLovin from Appodeal SDK. It is
included by default.
:::
:::info
For AppLovin, you can use either Appodeal Default Account or you can
connect your AppLovin Account to Appodeal. You can read more about these
options [here](/networks-setup/appodeal-vs-own-network-accounts).
**Appodeal Default Account:**
- You need to full fill the requirements, which you can find in your
App Settings -\> Mediation Settings -\> Line Items in your Appodeal
account.
**Your AppLovin Account:**
- Complete the steps below to link your AppLovin account.
:::
## Step 1. Prepare AppLovin account
Visit [this page](https://dash.applovin.com/signup) to
register a new account with AppLovin.
## Step 2. Create zones for required ad types
:::info
To create an interstitial zone, choose the **Non-Rewarded Full Screen**
ad type.
:::
## Step 3. Link your AppLovin account with Appodeal
:::info
At this moment you have created zones on the AppLovin network side.
:::
Now you need to add the values on the Appodeal side to finish linking
your account.
1\. Go to the [Mediation Ad Networks](https://app.appodeal.com/integrations/mediation_ad_networks).
2\. Find **AppLovin** in the list of ad networks.
3\. Click on the **Link your account** button.
4\. Fill in the following data:
- **email** **address** associated with AppLovin,
- **SDK Key,**
- **Report Key**
5\. Click the **Create** button.
:::info
**Email -** an email address that was used for the Applovin account
registration.
**SDK Key** and **Report Key -** to find it, log in to the Applovin
account and click on **Account** in the lower-left corner and go to the
**Keys** tab. **SDK Key** and **Report Key** can be found in the first
two fields.
:::
The account is ready to use.
## Step 4. Create line items for your app
It’s time to create line items for the app.
1\. Open **App Settings**.
2\. Open **Mediation settings**.
3\. Find the **AppLovin** ad network.
4\. Click on **Add Line item.**
Enter zone data in line item settings and click on **Create**:
:::info
**Zone ID** - Zone ID from AppLovin account. Navigate to **AppLovin
account** → **AppDiscovery** → **Zones.**
:::
**Default eCPM** should be set to the same value as on in the AppLovin
account when creating a zone. If a zone has **Optimized by AppLovin**
pricing setting, set **Default eCPM** to 0.
:::info
Keep the **Add to configurations** option enabled to add a line item in
the waterfall once it’s created.
:::
## Step 5. Update app-ads.txt file
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. If you implemented the **app-ads.txt** file, you should add Applovin
lines. To get the one open Applovin account and click on **Account**
in the lower-left corner and go to **app-ads.txt Info** tab and copy
and paste all the strings from there into your **app-ads.txt** file
generated by Appodeal.
2. After you have copied the lines from Applovin, you need to insert
them after the last line in our app-ads.txt file.
3. Save changes and wait at least 24 hours for ad networks to crawl and
verify your app-ads.txt file.
:::info
Make sure there are no duplicated rows in the file.
:::
Now you can monetize with AppLovin through Appodeal.
---
## IronSource
IronSource is known to be one of the mobile ad networks with the highest
CPM for publishers.
:::info Minimum requirements:
Make sure you haven't excluded IronSource from Appodeal SDK. It is
included by default.
:::
:::info
For IronSource, you can use either Appodeal Default Account or you
can connect your IronSource Account to Appodeal. You can read more about
these options [here](/networks-setup/appodeal-vs-own-network-accounts).
**Appodeal Default Account:**
- You need to full fill the requirements, which you can find in your
App Settings -\> Mediation Settings -\> Line Items in your Appodeal
account.
**Your IronSource Account:**
- Complete the steps below to link your IronSource account.
:::
## Step 1. Prepare IronSource account
Visit [this page](https://platform.ironsrc.com/partners/signup) to register a new account with
IronSource.
To connect IronSource to Appodeal you will need the Secret Key.
To get your **Secret Key** go to the **My Account** page and open an
**API** tab. There you can find your **Secret Key.**
## Step 2. Link your IronSource account with Appodeal
:::info
At this moment you should already have the key from the previous step.
:::
Now you need to add its value on the Appodeal side to finish linking
your account.
1\. Go to the [Network Accounts page](https://www.appodeal.com/apps/linked_networks).
2\. Find **IronSource** in the list of ad networks.
3\. Click on the **Link your account** button.
4\. Fill in a **secret key** and your email address associated with the
IronSource account.
5\. Hit the **Create** button.
6\. Mark the created account as default by clicking on the **Make
default** button.
## Step 3. Create a new IronSource app
1. You need to add your app if you haven't done so yet. You can do it
on [this page](https://platform.ironsrc.com/partners/applications/new).
To create a new app you should enter the **App URL**, choose the app
category, and press the **Add App** button.
Select the required Ad types.
2. Head to the **Payments** section on [this page](https://platform.ironsrc.com/partners/funds/payments/statements). You should fill in your payment and
company information. These steps are required in order to get real
IronSource ads.
3. Find your App key in your [Dashboard](https://platform.ironsrc.com/partners/dashboard).
:::info Please note
In order to go live you should contact IronSource
support as stated on their website and provide them your app's link to
the store.
:::
## Step 4. Create line items for your app
1. Open **Mediation settings** of the same app in the
[Appodeal dashboard](https://www.appodeal.com/apps).
2. In the **Line Items** tab of **Mediation settings** find the
**IronSource** ad network.
3. Change the account for this network from default to the one you
created earlier with this guide.
4. Enter the **App key** in the field **IronSource App Key** and press
the **Save** button.
5. Then, click on the **Add line item** button and set it up in the
same way as on the screenshot below.
:::info
Make sure that the newly created line item is marked as enabled (the
toggle is on “ON”).
:::
## Step 5. Update app-ads.txt file
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. If you implemented the app-ads.txt file, you should add the
IronSource account line.
To get one, read the [official documentation](https://developers.is.com/ironsource-mobile/general/app-ads-txt/#step-3).
2. After you have copied the lines from IronSource, you need to insert
them after the last line in our app-ads.txt file.
3. Save changes and wait at least 24 hours for ad networks to crawl and
verify your app-ads.txt file.
Now you can monetize with IronSource through Appodeal.
---
## Meta Audience Network
Suitable for any mobile app & game, with competitive eCPM for most ad
types.
Due to its broad reach, it's useful for global releases on the app
stores.
:::info
For Meta Audience Network, you can use either Appodeal Default Account or you can
connect your Meta Audience Network Account to Appodeal. You can read more about
Network Accounts [here](/networks-setup/appodeal-vs-own-network-accounts).
**Appodeal Default Account:**
- You need to fulfill the requirements, which you can find in your App Settings -\> Mediation Settings -\> Line Items in your Appodeal
account and for each app request Appodeal Meta Audience Network account connection if possible.
You can also request a connection using [Appodeal Default Account Connection (Admob or Meta)](/faq-and-troubleshooting/troubleshooting/general/technical-support-tickets) ticket type.
**Your Meta Audience Network Account:**
- Complete the steps below to link your Meta Audience Network account.
:::
:::info Before the start
- Make sure you have a personal Meta account.
- You should be able to provide full details for the payout account
that Meta Audience Network will pay to.
**Also, check your application:**
- Make sure you use Appodeal SDK 2.10.3+/Appodeal Unity plugin
2.14.5+.
- Make sure BidMachine is enabled in Mediation Settings.
- Make sure you have a BidMachine Adapter in your project. BidMachine
Adapter is included in the Appodeal SDK by default, make sure you
haven't removed it from the project.
If Meta Audience Network ad units have already been linked with
Appodeal, use [this guide](#meta-from-waterfall-to-bidding).
:::
## Step 1. Prepare Meta developer account
:::info
If you already have a Meta developer account, skip this step.
:::
Go to the [main page](https://developers.facebook.com) and press **Log in**.
Fill in your personal information and create a Meta account. After this,
you can log in and continue linking your Meta account to Appodeal.
## Step 2. Create a new Meta app
1. Go to **My Apps** and press the button **Create App**.
2. Select an app type based on your needs. Learn more about the types
[here](https://developers.facebook.com/docs/development/create-an-app/app-dashboard/app-types).
:::info
The app type can't be changed after your app has been created.
:::
In the example below the Business type has been chosen.
3. Fill in the following information:
- Display Name,
- App Contact Email,
- choose App Purpose,
- Business Manager account (if needed).
4. After creating the app, go to **Settings** → **Basic**.
:::info
You will need the following information from this page:
- **App ID,**
- **App Secret**.
Save **App ID** and **App Secret**, you will need them later.
:::
## Step 3. Configure the Monetization Manager account
Once the Meta app is created, you will need to connect it, if not
previously connected, with Meta Audience Network.
Go to app’s **Dashboard** and click **Set Up** for** Audience Network**.
Create a new **property** on the **Monetization manager**'s side.
Create a new app for the necessary platform in the created Property.
Click on **+Add** and proceed with the message notified that Meta is
working only with bidding for new apps.
Organize your products with properties.
If your product has multiple versions based on operating systems or
devices, you can group these versions into a property for combined
reporting and management.
Each property can contain one iOS app, one Android app, one website
domain, one Instant Articles Page, and one Instant Game.
For each property, you can create 4 ad spaces. Ad space is a location
for advertising.
You can create 4 placements for each ad space - a specific type of ad.
Setting up a new property consists of 4 stages:
1\. **Add app details**. If your app is already published, specify a
link to the store. You can add the URL later if your app isn't live.
2\. **Add a payment account.** Click on **Add a new payment
account** and proceed with your payment data.
You won't be able to receive any Audience Network ads until you add your
payment details. You can add your payment info as soon as your account
in Business Manager and Property is set up. [Learn more](https://web.facebook.com/business/help/915454841921082?id=180505742745347&_rdc=1&_rdr) about adding payment information.
3. **Verify your business**.
[Learn more](https://www.facebook.com/business/help/2058515294227817?id=180505742745347)
about business verification.
4. **Integrate SDK.**
a. Choose **A different mediation platform**.
b. **Create Placement**. You need to select the desired ad format. The
most effective ad formats for Meta are Interstitial and Rewarded video
(this one is for game apps only).
Click on **Create** and you will see your **App ID**, and **Placement
ID**’s.
As a result of the previous steps, you should have the following data:
- **App Id;**
- **App Secret;**
- **Placement Ids.**
Save this data. You will need it to bind the app on the Appodeal side.
:::note Make sure
After you have created your app, set up monetization, published it
and [submitted for review](https://developers.facebook.com/docs/app-review/submission-guide?locale=en_US),
added the link to the privacy policy (**App Settings → Basic**) and user data
deletion (you can add the same as the link to the privacy policy),
you need to switch the app state to **Live**.
:::
## Step 4. Link your Meta Audience Network account with Appodeal
Go to the [Networks Accounts](https://www.appodeal.com/apps/linked_networks) page and add a Meta email
address.
## Step 5. Create line items for your app
Select the appropriate app on [this page](https://www.appodeal.com/apps),
go to the desired ad type, select a Meta account created in Networks
Accounts and fill in the Meta **App ID** and Meta **App Secret** fields.
Next, click the **Add Line Item** button, enable **In-App Header Bidding
mode** and fill in the **Line Item’s label** and **Code**:
:::info
It is necessary to set up **In-App Header Bidding** mode, otherwise
**Meta Audience Network** ads won’t work.
The code is a placement id. The code must have the following format:
If the first part of the placement ID does not match the app ID, Meta
Audience Network won't work.
To fix it, change the first part of the placement ID to the app id.
:::
## Step 6. Test your Meta Audience Network ads
:::info
Meta Audience network bidding works through BidMachine. If another
BidMachine demand partner rather than Meta Audience has an ad with a
higher bid for you, it will be shown, even with Meta Audience test mode
enabled.
:::
To debug Meta Audience Network ads, check the placement statuses:
- **Ready to publish**: You’ve successfully tested your integration
and your app is ready to be published to the app store.
- **Idle**: Meta Audience Network hasn’t received any ad requests for
this placement in the last 24 hours. If you're sending test
requests, the placement status will remain "Idle".
- **Requesting Ads**: No fill for ad requests for the placement in the
last 24 hours.
- **Receiving Ads**: Your app is live and there are filled ad requests
from live users for the placement in the last 24 hours.
You can see a list of common errors when requesting Meta Audience
Network ads [here](https://developers.facebook.com/docs/audience-network/guides/test/checklist-errors/).
:::info Please note
If Meta has a low number of impressions and small
revenue, the data can be reported from Meta without country info.
If you have any additional questions or difficulties during the setup
process, you can contact our support team via live chat or email
support@appodeal.com.
:::
## Step 7. Update App-ads.txt file
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. Go to [Meta Business Settings](https://business.facebook.com/settings/info)
and go to Business info.
2. Copy your Meta Business Manager ID and replace the “Business ID”
with it in your app-ads.txt file.
``` xml
#Facebook
#Replace the Business ID below with your Facebook Business ID
facebook.com, Business ID, RESELLER, c3e20eee3f780d68
```
Example:
3. Save changes and wait at least 24 hours for ad networks to crawl and
verify your app-ads.txt file.
Now you can monetize with Meta through Appodeal.
## Switching Meta Audience Network from waterfall mode to bidding mode {#meta-from-waterfall-to-bidding}
:::info Before the start
- Make sure you use Appodeal SDK 2.10.3+/Appodeal Unity plugin
2.14.5+.
- Make sure BidMachine is enabled in Mediation Settings.
- Make sure you have a BidMachine Adapter in your project. BidMachine
Adapter is included in Appodeal SDK by default, make sure you
haven't removed it from the project.
- Starting on 1 September 2022, Meta is fully migrating to using FB
Login to access Reporting API. Due to this, please re-connect your
account to apply the necessary changes and continue receiving the
revenue stats from Meta Audience Network in Appodeal dashboard. Go
to Integrations -\>
[Mediation Ad Networks](https://app.appodeal.com/integrations/mediation_ad_networks) and click on “Re-connect”
next to your Meta Audience Network account.
:::
## Step 1. Choose line items
Choose **one line item for each ad type with the highest revenue**. You
will need to add them to Header Bidding later.
1\. Go to your **Dashboard**.
2\. Create a report with filters **App**, **Ad Network** (select Meta Audience
Network), split by **Line Items** and **Ad Type**. Add **Ad Revenue** measure.
You can use [this template](https://app.appodeal.com/analytics/reports?q=(f~(APP~!~DATE~(from~*2021-11-01~to~*2021-12-31)AD*_NETWORK~!130)g~!AD*_TYPE~LINE*_ITEM~~m~!ad*_revenue~~view~table~fv~*))
to generate a report. Select your app in it.
## Step 2. Set up line items
Go to **App Settings** → **Mediation Settings** → **Line Items.** Select
the ad type and open the Meta Audience Network line items list.
**Save the code** for chosen Line Item and remove all the Meta Audience
Network Line Items.
## Step 3. Create a new in-app header bidding line item.
1\. Click the **Add Line Item** button.
2\. Enable **In-App Header Bidding** **mode**.
3\. Fill in the **Line Item’s label**, **default eCPM**.
4\. Use the same **Line Item Code** (placement ID) of the ad unit chosen
in step 1 and saved in step 2.
:::info
It is necessary to set up In-App Header Bidding mode, otherwise Meta
Audience Network Audience ads won’t work.
The **Code** is the placement id. The **Code** must have the following
format:
If the first part of the placement ID does not match the app ID, Meta
Audience Network won't work.
To fix it, change the first part of the placement ID to the app id.
:::
---
## Unity Ads
Unity Ads is a great video ad network that allows you to quickly and
effectively monetize your apps.
:::info Minimum requirements:
Make sure you haven't excluded Unity Ads from Appodeal SDK. It is
included by default.
:::
:::info
For Unity Ads, you can use either Appodeal Default Account or you
can connect your Unity Ads Account to Appodeal. You can read more about
these options [here](/networks-setup/appodeal-vs-own-network-accounts).
**Appodeal Default Account:**
- You need to full fill the requirements, which you can find in your
App Settings -\> Mediation Settings -\> Line Items in your Appodeal
account.
**Your Unity Ads Account:**
- Complete the steps below to link your Unity Ads account.
:::
## Step 1. Prepare Unity Ads account
1. Create a Unity account
using [this link](https://id.unity.com/en/). If you already have a Unity account,
skip this step.
2. Confirm your account via email.
3. Go to the Unity dashboard and accept the Terms of Service Agreement.
## Step 2. Create a new Unity Ads app
Go to **Unity Dashboard** → **Projects** and click **Create project**
button.
If your app supports both Android and iOS platforms, you don’t need to
add each of them separately.
## Step 3. Configure Monetization
Go to **Explore Services** → **Monetization** and press **Set up**
button.
Then in **Monetization** section press **Get Started**.
## Step 4. Create placements for your app
Select **Monetization** on the left and go to the **Ad Units** and
**Placements**.
An Ad Unit represents the ad format settings for a surfacing point in
your game, or for a collection of Placements using the same format
across multiple surfacing points.
Ad Units and Placements are created for you by default. If you want, you
can create your own.
Click on the ad unit name to change its settings.
Go to **Monetization** → **Placements** and here you can create your own
placements by pressing **Add Placement**
## Step 5. Get Reporting API Key and Organization ID from your Unity account
The API uses a key from the Developer Dashboard. To find it:
1. To get the API key go to **Monetization** → **Setup** → **API
Management** and then you can copy your API Key.
2. To get Organization ID go to **Monetization** → **Setup** →
**Organization Settings** and copy **Organization core ID.**
## Step 6. Link your Unity Ads account with Appodeal
1. Follow this [link](https://www.appodeal.com/apps/linked_networks).
2. Go to **Unity** and press **Link your account**.
3. Add **email** for Unity account, **API Key** and **Organization
ID**.
4. Click on the **Create** button.
## Step 7. Create line items for your app
Go to **Monetization** → **Ad Units** and copy your **Game ID**.
Go to [Appodeal apps](https://app.appodeal.com/apps) →
**App's settings** (little gear) → **Mediation settings**, select the ad
type. Choose your Unity account and fill in the **Unity Game ID**.
Then go to **Monetization** → **Placements** and copy the **placement
ID**.
Add a line item on Appodeal side. Go to **Line Items** in the app's
settings, select the ad type, and press **Add Line Item** button.
Add **Line Item label**, **Countries**, **eCPM,** leave **"Add to
configurations"** turned on, and fill in the **Code**.
:::info
The **Code** is a Unity Placement Reference ID.
:::
:::info
Remember to save the changes.
:::
## Step 8. Update app-ads.txt file
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. If you implemented the app-ads.txt file, you should add the Unity
Ads account lines. To get ones, read the
[official documentation](https://docs.unity.com/ads/app-ads-txt.html).
2. After you have copied the lines from Unity Ads, you need to insert
them after the last line in our app-ads.txt file.
3. Save changes and wait at least 24 hours for ad networks to crawl and
verify your app-ads.txt file.
Now you can monetize with Unity Ads through Appodeal.
---
## VK Ads
VK Ads is the Top 20 global advertising network.
:::info Minimum requirements:
Make sure you haven't excluded VK Ads from Appodeal SDK. It is
included by default.
:::
:::info
For VK Ads you can use either Appodeal Default Account or you can
connect your VK Ads Account to Appodeal. You can read more about these
options [here](/networks-setup/appodeal-vs-own-network-accounts).
**Appodeal Default Account:**
- You need to fulfill the requirements, which you can find in your
App Settings -\> Mediation Settings -\> Ad Units in your Appodeal
account.
**Your VK Ads Account:**
- Complete the steps below to link your VK account
:::
## Step 1. Prepare VK Ads Account
1. [Register](https://ads.vk.com/en/help/articles/partner_registration#new) in VK Ads service
as a publisher or import an existing myTarget account as described [here](https://ads.vk.com/en/help/articles/partner_import_mt).
If you already have a VK Ads account, skip this step.
:::info IMPORTANT!
The VK Ads account must be initially set as an **Advertiser** account.
You can't change type of VK Ads account once it's created.
Please choose USD currency on VK Ads side as Appodeal uses **USD** currency.
:::
2. Add apps and ad units you want to monetize with VK Ads. Go to **Apps** and
click the [Add app](https://ads.vk.com/hq/partner/mob_apps) button:
3. Paste your **app store URL** (App Store or Google Play) and all the data such as the app icon and the app name will be automatically pulled up.
:::note
After you have added an app, you will see a gray indicator, which means that your app is under [moderation](https://ads.vk.com/en/help/articles/partner_moderation).
The app should be reviewed within **24 hours** on business days.
:::
4. You can start creating your first ad units now, without waiting for the moderation to complete.
:::note
Ads will appear in your app and generate revenue only after your app passes
[moderation](https://ads.vk.com/en/help/articles/partner_moderation) and your
[payment details](https://ads.vk.com/en/help/articles/partner_registration#requisites) are verified.
At that point, your app will be marked as Active.
:::
Go to **Apps** tab → select your app → navigate to **Placements** and click on **Add placement**.
5. Enter the description of the ad unit, choose the required ad format, and select Direct Integration or
In-App Bidding if you want to use bidding, then click **Create**.
:::note
An automatic CPM is set by default.
You can specify the minimum eCPM cost by choosing the Manual option for **CPM floor**.
:::
6. The new block will appear in the list on the **Placements** tab.
7. After the successful completion of moderation, the status of the placement will change to "Active".
8. Select **Add placement** to create more for different ad types.
## Step 2. Link Your VK Ads Account With Appodeal
1. Get [Token](https://ads.vk.com/hq/settings) from your VK Ads account.
:::info
VK Ads provides reporting API, which helps to get VK Ads performance
data for publisher sites and apps automatically.
:::
To obtain Reporting API access you need to get an access token:
- Open Settings → [General](https://ads.vk.com/hq/settings) in VK Ads account.
- Create new or get a current **Statistics API token**.
- Copy the token.
2. Add your VK Ads account to Appodeal. To do this follow [this link](https://www.appodeal.com/apps/linked_networks)
→ go to **VK Ads** → Link your account and add **email** for VK Ads account and
**Statistics API** token.
## Step 3. Create Ad Units For Your App
1. Go to **Ad Units** in your app settings.
:::info
You need the following information to link your app and ad unit:
- App ID
- Placement ID
You can find all that info on the **Apps** → **Placements** tab in your VK Ads account.
:::
2. Choose your VK Ads account, fill in the **App ID** and press the **Add Ad Unit** button.
3. Fill in the following information:
- **Ad Unit label** (a name for your ad unit in Appodeal dashboard)
- **Countries**
- **eCPM** (ad unit's position in the waterfall, if you have set eCPM on VK side then enter it here or leave 0)
- **Placement ID**
:::info IMPORTANT!
Please check the currency of VK CPM floor and set it to **USD** as Appodeal uses **USD** currency.
:::
4. Make sure to choose the correct mode. If you have bidding mode on VK Ads side then turn on the toggle to use bidding.
5. After creating the ad unit, press **Create**.
:::info
Make sure that the newly created ad unit is marked as enabled (the
toggle is on “ON”).
:::
## Step 4. How To Receive Live Ads
:::info
VK Ads serves ads only to users in Russia and the CIS.
:::
1. Make sure your app has been [moderated](https://ads.vk.com/en/help/articles/partner_moderation),
the [status](https://ads.vk.com/en/help/articles/partner_create_app#status) next to your app should be **green**.
2. Make sure to fill out the [payment details](https://ads.vk.com/en/help/articles/partner_registration#requisites).
3. Make sure you have selected the correct type of integration:
- **Direct Integration** - for classic mode
- **In-App Bidding** - for bidding mode
4. Then you can launch your **published** application and try to show VK ads.
## Step 5. Update App-ads.txt File
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. If you implemented the app-ads.txt file, you should add the VK Ads
account line. To get one, read the [official documentation](https://ads.vk.com/en/help/articles/partner_ads_txt)
and copy the strings from your Account Settings → [General](https://ads.vk.com/hq/settings).
2. After you have copied the lines from VK Ads, you need to insert
them after the last line in our app-ads.txt file.
3. Save changes and wait at least 24 hours for ad networks to crawl and
verify your app-ads.txt file.
Now you can monetize with VK Ads through Appodeal.
---
## Vungle
Vungle is a great ad network, especially for video ads.
:::info Minimum requirements:
Make sure you haven't excluded Vungle from Appodeal SDK. It is included
by default.
:::
:::info
For Vungle, you can use either Appodeal Default Account or you can
connect your Vungle Account to Appodeal. You can read more about Network Accounts [here](/networks-setup/appodeal-vs-own-network-accounts).
**Appodeal Default Account:**
- You need to full fill the requirements, which you can find in your
App Settings -\> Mediation Settings -\> Line Items in your Appodeal
account.
**Your Vungle Account:**
- Complete the steps below to link your Vungle account.
:::
## Step 1. Prepare Vungle account
Create a Vungle account using [this link](https://vungle.com/signup/).
Choose **Monetization** as a Job Position.
Confirm your account via email and go to the Vungle dashboard.
If you already have a Vungle account, skip this step.
## Step 2. Create a new Vungle app
Go to **Dashboard**, click on the **Add Application** button. If your
app supports both Android and iOS platforms, you need to add each of
them separately.
Select a platform for your app, add the app’s name. Choose the app’s
state in the Store.
- If the app is live in the store, choose **My app is live** and use a
search field to add the link.
- If the app is not live yet, you will receive test ads. You can
connect the store link at any time later in the app’s settings.
:::info
If your app is designed for children under 13, tick the checkbox
Disagree in Apps Directed Toward Children Under Age 13.
:::
Click the **Continue** button.
## Step 3. Create placements for your app
:::info
Vungle banners and MREC are supported by Appodeal starting from Appodeal
SDK 2.11.0+.
:::
Select the ad type and fill the name of the placement. For rewarded
video, choose "No" for a Skippable setting.
Click on the **Continue** button and click on the button **I’ve already
integrated the most recent SDK** and press **Sounds Good** on the next
page (see the screenshot below).
## Step 4. Link your Vungle account with Appodeal
In order to get **API Key** from your Vungle account, click on the
**Settings** button at the top-right corner, choose **My Account** and
copy **API Key**:
Now you can add an email for the Vungle account and Reporting API Key
for Appodeal.
1. Go to [Networks Accounts](https://www.appodeal.com/apps/linked_networks) page.
2. Go to **Vungle** and click on **Link your account**.
3. Add email for Vungle account and Reporting API Key.
4. Click on the **Create** button.
## Step 5. Create line Items for your app
Go to **Applications** on the Vungle side and click on **App ID** to
copy.
Add **Vungle** **App ID** on Appodeal side. Go to **Line Items** in the
app's settings and select the ad type. Choose your Vungle account and
add the **Vungle App Id**.
Go to the **App’s settings** on the Vungle side and click on **View
All** for placements (see the screenshot below).
Click on **Placement Reference ID** to copy.
Go to **Line Items** in the app's settings, and select the ad type.
Press the **Add Line Item** button, add **Line Item label**,
**Countries**, **eCPM,** and choose either **Default only** or **All
configurations**, add **Code**.
:::info
**Code** is a Vungle Placement Reference ID.
:::
After creating all the line items, save the changes.
## Step 6. Update app-ads.txt file
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. If you implemented the app-ads.txt file, you should add the Vungle
account line. To get one, read the [official documentation](https://support.vungle.com/hc/en-us/articles/360029177591--Vungle-app-ads-txt-entries#step-1-provide-the-publisher-website-url-in-your-app-store-listing-0-5).
2. After you have copied the lines from Vungle, you need to insert
them after the last line in our app-ads.txt file.
3. Save changes and wait at least 24 hours for ad networks to crawl and
verify your app-ads.txt file.
Now you can monetize with Vungle through Appodeal.
---
## Yandex
Yandex is a large ad network with quality ads.
:::info Minimum requirements:
Make sure you haven't excluded Yandex from Appodeal SDK. It is included
by default.
:::
:::info
For Yandex you can use your Yandex Account. You can read more about Network Accounts [here](/networks-setup/appodeal-vs-own-network-accounts).
To connect your Yandex account to Appodeal, follow the steps below.
If you are not able to create your Yandex account you can [create a ticket](/faq-and-troubleshooting/troubleshooting/general/technical-support-tickets) of type **Appodeal Default Account Connection**.
:::
## Step 1. Prepare Yandex account
:::info
If you already have a Yandex account, proceed to Step 2.
:::
1. Create a free Yandex account using
[this link](https://partner.yandex.ru/).
2. Complete the registration form.
3. In the **What do you want to monetize?** section, choose **Apps** or
**Both**.
4. Click **Register**.
## Step 2. Create a new Yandex app
Now you need to add the app, which you want to monetize with Yandex
Advertising Network.
1. Go to **Ads in apps** and click on the **Add app** button.
2. Add your app's store URL.
:::info
It may take a few days for Yandex to approve your app after adding the
store link.
:::
If you haven't published the app yet, click on the **My app is not
published**, choose the platform and add the app name.
3. Click on the **Add** button to create your Yandex app.
4. You will see your **Yandex App ID** to the right of your app name.
Save it, you will need it later.
You can also find your **Yandex App ID** later on the **Ads in apps** to
the right of your app name.
## Step 3. Create ad units on Yandex side
Create the first ad unit for your app.
1. Choose the **ad unit type**.
:::info
At the moment, Appodeal supports Banner, Interstitial and Rewarded video
for Yandex.
:::
2. Fill in the **ad unit name**.
3. Choose the **CPM type**. There are two ways to set a CPM floor:
- **Auto CPM**. Yandex dynamically sets your floors for best ad
performance based on all available data.
- **CPMV floor.** You can manually set the value of the minimum CPMV
for each ad unit. CPMV is a cost of one thousand viewable
impressions.
:::info IMPORTANT!
If you want to set the Yandex Minimum CPMV floor, keep in mind that by
default Yandex uses RUB, not USD.
Currency can be changed in **Yandex Advertising Network** → **Settings**
→ **Currency of CPM floors**.
:::
4. Click on the **Create ad unit** button to save the changes.
You will be able to set up other ad units at **Ads in apps** → **Apps**
→ **\[Your app name\]** → **Create ad unit**.
5. After creating the ad units, go to **Ads in apps** → **Apps** →
**\[Your app name\]**, you will see your ad units list. Save your ad
units’ codes, you will need them later.
## Step 4. Get Yandex OAuth Token to export stats
This token is necessary for importing reports from your Yandex account to the Appodeal dashboard. This is important for effective mediation.
1. Go to [Yandex.OAuth](https://oauth.yandex.com/client/new) and register a new client.
:::note
The steps below are only accessible using our link above ⬆️, or you can copy it here ➡️ https://oauth.yandex.com/client/new,
do not navigate the Yandex site yourself.
:::
2. Under the Platforms section select the Web services checkbox and paste the URL: https://oauth.yandex.com/verification_code
3. In the Data access choose `pi:all` (Use Yandex API partner interface).
4. After saving the changes you'll see your **Client ID**, **Client Secret** and **Redirect URL**(see the example below).
5. Now, you can manually get a token for the user. As a user, you can use the same developer account you have used to register your app and apply for access.
To get a token manually, follow these steps:
- Log in to Yandex with your username.
- Before going to [this page](https://oauth.yandex.ru/authorize?response_type=token&client_id=ID) see the warning below.
:::warning IMPORTANT
Replace **ID** in the link `https://oauth.yandex.ru/authorize?response_type=token&client_id=ID` with your **ClientID** from the step 4.4.
:::
- On the page that opens, log in to your Yandex account.
- **Yandex.OAuth** will redirect you to the page displaying the token. The token will also be added to the URL shown in the address bar.
## Step 5. Set up the AppMetrica app
1. Go to [AppMetrica](https://appmetrica.yandex.com/). Login with your Yandex
account.
2. Add your app. If you have already added your app to AppMetrica,
proceed to point 5.6.
3. Fill in the following data:
- the app name,
- choose the app's category,
- add the store link to your app.
4. Click **Continue**.
5. Choose your **Time Zone**, accept the data processing conditions and
enter your email.
6. Click **Go to Reports**.
7\. Go to [AppMetrica](https://appmetrica.yandex.com/) →
**Applications** tab and choose the app. In the **Settings**, you'll see
**Application ID** and **API Key (for SDK)** in the General settings.
Save **Application ID** and **API Key** (for SDK), you will need them
later.
## Step 6. Link your Yandex account with Appodeal
:::info
**After completing all the previous steps**, you should have the
following data, which is necessary for linking Yandex with Appodeal.
Numbers in brackets refer to the corresponding instruction points:
- Yandex email (1.1);
- Yandex OAuth token (4.5d);
- Yandex Advertising Network app ID (2.4);
- AppMetrica Application ID (5.6);
- AppMetrica API Key (for SDK) (5.6);
- Ad units codes (3.5).
:::
1. Link your Yandex account with Appodeal
- Follow [this link](https://www.appodeal.com/apps/linked_networks).
- Go to **Yandex** → **Link your account**.
- Add your **Yandex email** and **Yandex OAuth token**.
- Click **Create**.
2. Link your Yandex App & Create Line Items
- Select the app you wish to link
[here](https://www.appodeal.com/apps).
- Go to the desired ad type, select the Yandex account you created.
- Fill in the following data:
1. **PageID** with **Yandex Advertising Network app ID**;
2. **AppMetrica Application ID;**
3. **AppMetrica API Key (for SDK).**
- Click **Add Line Item** and fill in:
1. The **Line Item's label** - a name for your ad unit in the
Appodeal Dashboard.
2. **Default eCPM** - ad unit's start position in the waterfall. In
automatic priorities configuration, it will be recalculated
later based on your stats.
:::info
For Auto CPM ad units, you can set any Default eCPM.
For CPMV floor ad units, set Default eCPM based on the ad unit's CPM
threshold on Yandex.
Default eCPM on Appodeal side is in **USD** but Minimum CPM threshold on
Yandex Advertising Network's side is in **RUB** by default.
For line items to be set up correctly, don't forget to convert RUB to
USD or change the currency in the **Yandex Advertising Network** →
**Settings** → **Currency of CPM floors**.
:::
**Code** - your Yandex ad unit ID.
**Device Type** (for banners) - phone or tablet.
- After creating a line item, save the changes.
## Step 7. Update app-ads.txt file
:::info
Be sure to set up [app-ads.txt](/advanced/app-ads) to
help bidders and advertisers identify whether or not ad inventory is
being sold by authorized sellers.
:::
1. If you implemented the app-ads.txt file, you should add the Yandex
account lines. To get ones, read the [official documentation](https://ads.yandex.com/helpcenter/en/monetization/app/ads-txt).
2. Save changes and wait at least 24 hours for ad networks to crawl and verify your app-ads.txt file.
Now you can monetize with Yandex through Appodeal
---
## Ad Units
## Waterfall algorithm explanation
Whenever a user opens an application, we send a list of ad units (or
waterfall) to the device. All the ad units in the waterfall are
prioritized based on historical eCPMs. While caching we fire all ad
units one by one until one of them returns with an ad creative. With
this approach, we ensure the best possible eCPM. Moreover, this
guarantees a solid fill rate.
Actually, we have two waterfalls. The second one is the waterfall that
is described above. And another one is used to serve ads as soon as
possible. This waterfall contains the cheapest ad units with the best
fill rate performance. So if you call “show method” right after the
initialization of our SDK, ads will be shown almost immediately.
Otherwise, we will initiate the ordinary waterfall to fill an app
inventory with the most profitable ads.
## In-App Header Bidding algorithm explanation
In-App Header Bidding works as a unified auction aimed at finding the
highest-paying ad among all the demand sources in real time, making
their opportunities equal. In-App Header Bidding provides closer access
to the actual highest price per impression for publishers.
In addition to that, in-app header bidding significantly reduces latency
and invigorates the ad delivery process. The ad requests and the
auctions are processed on the server side.Therefore, devices no longer
need to run consecutive client-side ad requests.
**Ad Units** tool provide an opportunity to create and manage enable or
disable ad units for each Ad Network.
**Priorities** give you the power to create custom waterfalls. With this
functionality, you can manage Ad Units order using one of four modes:
in-app header bidding, manual, eCPM, and automatic, or you can use all
of them at the same time. Moreover, you can create waterfalls for each
of the countries, or you can even target the waterfall for certain user
segments.
:::note
Priorities setting is available only with Appodeal SDK 2.6.1+
:::
## Ad Units
To find this tool:
1. You should navigate to the **Apps** page
2. Choose the app you want to manage Ad Units for
3. Click on the three dots on the right to see the options
4. Select the **Mediation Settings** tab. For your convenience, we divided Ad Units by the ad types.
This tool enables you to create and manage Ad Units for all the
supported ad networks, but it also gives you the power to turn the ad
networks on and off when you need to.
Here you can find our special ad network with zero eCPM - **Backfill**. It is needed only at the start of mediation to increase
the fill rate while other ad networks connect. You don't get any revenue
for these impressions. Backfill is automatically disabled after 1000
impressions.
To create a new Ad Unit for the specific ad network & ad type, you
should extend the list of Ad Units for this ad network and click the
**Add Ad Unit** button, or you can click on the gear icon to manage
the settings for existing Ad Units.
For every Ad Unit (including those created by Appodeal), you can
specify the countries where this Ad Unit will be added to the
“waterfall”. Choose countries from the list for even more precise
targeting.
Default eCPM settings determine the start position of the ad unit in the
waterfall. Once we have enough data from the ad network side, we're able
to analyze their eCPM tendencies and predict which eCPM would be more
effective.
In the **code** field, you should fill the unique identifier of this ad
unit.
## Priorities
This tool is located right next to the Ad Units tab.
Priorities is a feature that allows you to move Ad Unit positions
around with the “drag and drop” function and organize them into
country/segment groupings that are easy to create. In other words, you
can manage your waterfall configuration in a number of ways. Whenever
you create a new app, we automatically add “Default configuration,”
where all the Ad Units are located in the Automatic Priority section.
By default, this configuration is applied to all countries/segments. If
you add the Ad Unit to the custom priority, it will override the
"countries" setting for this Ad Unit. Thus it can work for the country
which isn't selected in the settings of this Ad Unit. You can create
new configurations for the specific ad type by clicking the “New
Configuration” button.
First, you need to add the name of your configuration. We advise you to
name it by the countries/segments names you want to target this priority
to.
Then you need to choose whether you want to target this configuration to
a range of countries or for some segments. You can select one country as
a group or a number of countries for the configuration. However, you
can't use the same country in several groupings (for example, if you
choose Canada, USA, and Australia for one of the country groupings, you
can't feature Canada in another grouping).
The same principle applies to the Segments targeting.
Finally you need to configure the ad units positioning in the waterfall
for the selected ad type. There are four modes you can utilize:
- In-App Header Bidding
- Manual Priority
- Automatic Priority
- eCPM Priority
On the left, you can see the tab **Unused Ad Units** — that's where
the newly created and previously unused Ad Units are placed, so you
can drag all the Ad Units you'd like to use for Priority Modes. Keep
in mind that in this section you can't create new Ad Units. For that,
you'd need to use the **Ad Units** tool.
In most cases, it's recommended to add Ad Units to the **Automatic
Priority,** where they are positioned automatically (based on the
statistics at our disposal). You can also use the “Move all unused line
items here” button to move all the ad units from the Unused Ad Units
section to the Automatic Priority. Note that Ad Units in this section
are sorted by their names.
**eCPM priority** is in-between Automatic and Manual priorities and
allows specifying eCPM for each Ad Unit for further automatic
positioning.
In **Manual Priority**, you can arrange top positions: Ad Units that
go first. Manual Priority is basically for your preferred Ad Units
that you want to “release” as a priority in the waterfall. You can
choose up to 3 Ad Units for one position. In fact, it means that every
time will be randomly selected one of the specified ad units and placed
in the corresponding position in the waterfall.
**In-App Header Bidding** **priority** should be used only for in-app
header bidding Ad Units if you have created them previously in the
Ad Units section.Here you can either set up price floors manually
or select the **Auto Price Floors** option.
:::caution
Please choose **Auto Price Floors** option for In-App Header Bidding for
now.
:::
:::info
Not all ad networks support in-app header bidding.
:::
All four modes are functioning simultaneously.
If a Ad Unit is displayed in the list in a muted state, that means it
can't be added to the waterfall associated with this priority for the
reason this Ad Unit was switched off in the Ad Units tool.
## Popular use cases
In this section, you can find some basic use cases of our Networks Setup
separated by tools.
**Priorities**
1. If you have a direct deal with an ad network, and you agreed on the
position of the network in the waterfall, you should use the manual
section to obtain the necessary results.
2. Sometimes ad networks cannot display your statistics for different
reasons. In this case, the positions of Ad Units for this ad
network will be automatically changed by our algorithm unless you
put them in the manual or eCPM sections.
3. If you are not sure which section suits you best, use the automatic
priority section.
4. You can use segment targeting to conduct A/B tests of two waterfalls
for one country (by adding “country” and “part of audience” filters
in the segments).
5. To use the same waterfall configuration for a range of countries,
you can target priority to these countries instead of creating
priorities for each of them.
**Ad Units**
If an ad unit performs poorly, you can easily disable it using Line
items, and this change will apply for all of the priorities.
---
## Glossary
**Ad unit** - a unique entity that should be created on
the ad network side to serve ads.
**Waterfall Configuration** - a unique entity that
manages ad units positions and priorities.
**eCPM (Effective Cost Per Mille)** - the metric that
helps measure the generated ad revenue from 1000 ad impressions.
**Backfill** - is a special ad network with zero eCPM.
**Waterfall** - a list of ad units sorted from top to bottom
based on the historical eCPMs. With waterfall, when
requesting an ad to fill, the best-performing ad networks
appear at the top of the waterfall.
**Network account** - an account of the specific ad network
that an app developer can link to their Appodeal account
to simplify the process of showing ads.
---
## Introduction
Networks Setup is a tool meant to increase transparency
when working with Appodeal and give you maximum control
over mediation. Networks Setup allows you to keep track
of activated networks in your applications, to manage
ad units (add and delete, as well as switch their priority
in the waterfall) and independently link your own ad network
accounts to the Appodeal account. Now you can manually
adjust the position of ad units where it is necessary.
At the same time, some networks can continue operating
in an automatic mode.
The tool consists of three parts: Mediation Ad Networks,
Ad Units, and Mediation Groups.
**Mediation Ad Networks** settings allow you to manage linked
ad network accounts to be used within applications.
You can independently link your accounts and subsequently
use them in applications. For most networks, Appodeal accounts
are used by default, but you can always add your own.
After linking, you can enable them in your applications.
Related income reports will be aggregated for you in the
Appodeal dashboard. You can also filter your accounts based
on the mediation engine.
**Ad Units** provide an opportunity to enable or disable ad networks,
choose an ad network account for each network, and manage ad units
created in these networks for the application.
**Mediation Groups** give you the power to create custom waterfalls
for each mediation. With this functionality, you can manage
ad units order using one of four modes: in-app header bidding,
manual, eCPM, and automatic, or you can use all of them
at the same time. Moreover, you can create waterfalls for each
of the countries, or you can even target the waterfall for certain
user segments.
:::note
Applovin MAX mediation is available only with Appodeal SDK 3.3.0+,
LevelPlay - with Appodeal SDK 3.5.0+.
:::
---
## Mediation groups
**Mediation groups** is located right next to the Ad Units tab.
Mediation Groups are used to streamline targeting and mediation settings
that can be applied across different ad types within the same mediation
engine. They simplify the mediation process by enabling a single set of
configurations to be applied automatically to relevant waterfall setups based
on predefined targeting. Using Mediation groups you can move ad unit positions
around with the “drag and drop” function and organize them into country/segment
groupings that are easy to create. In other words, you can manage your waterfall
configuration in a number of ways. Whenever you create a new app, we automatically
add a “Default” mediation group, where all the ad units are located in the Automatic
Priority section for each ad type. By default, this mediation group is applied
to all countries/segments. If you add the ad unit to the custom priority, it
will override the "countries" setting for this ad unit. Thus it can work for
the country which is not selected in the settings of this ad unit. You can
create new mediation groups for the specific mediation or targeting by
clicking the “New Mediation group” button.
First, you need to add the name of your mediation group. We advise you
to name it by the countries/segments names you want to target this
priority to including the mediation engine it will use.
Then you need to choose whether you want to target this mediation
group to a range of countries or for some segments. You can select
one country as a group or a number of countries for the configuration.
However, you can't use the same country in several
groupings (for example, if you choose Canada, USA, and Australia
for one of the country groupings, you can't feature Canada in another grouping).
The same principle applies to the Segments targeting.
Then you need to choose the mediation you plan to use for this mediation group.
After that click “Save” to create a mediation group. Waterfall configurations
for each ad type for saved mediation groups will be created automatically.
Finally you need to actualize the ad units positioning in the waterfall for all ad types.
If you chose Appodeal mediation, there are four modes you can utilize:
- In-App Header Bidding
- Manual Priority
- eCPM Priority
- Automatic Priority
On the left, you can see the tab **Unused ad units** — that's where the newly created
and previously unused ad units are placed, so you can drag all the ad units
you'd like to use for Priority Modes. Keep in mind that in this section
you can't create new ad units. For that, you'd need to use the **Ad Units** tool.
In most cases, it's recommended to add ad units to the **Automatic Priority**,
where they are positioned automatically (based on the statistics at our disposal).
You can also use the “Move all unused ad units here” button to move
all the ad units from the Unused ad units section to the Automatic Priority.
Note that ad units in this section are sorted by their names.
**eCPM priority** is in-between Automatic and Manual priorities
and allows specifying eCPM for each ad unit for further automatic positioning.
**In Manual Priority**, you can arrange top positions: ad units that go first.
Manual Priority is basically for your preferred ad units that
you want to “release” as a priority in the waterfall.
You can choose up to 3 ad units for one position. In fact, it means that every
time will be randomly selected one of the specified ad units
and placed in the corresponding position in the waterfall.
**In-App Header Bidding** priority should be used only for in-app header bidding ad units if you have created them previously in the ad units section. Here you can either set up price floors manually or select the Auto Price Floors option.
:::caution
Please choose **Auto Price Floors** option for In-App Header Bidding for now.
:::
:::info
Not all ad networks support in-app header bidding.
:::
All four modes are functioning simultaneously.
If an ad unit is displayed in the list in a muted state, that means
it can't be added to the waterfall associated with this priority
for the reason this ad unit was switched off in the Ad Units tool.
If you choose Applovin MAX or LevelPlay mediation during mediation
group creation, there is only one mode you can utilize - Automatic priority.
If you plan to use the Amazon ad network with custom mediations,
you need to place the Amazon unit in the Amazon section.
You can’t create mediation ad units on your own, ad units will be added
automatically after your app meets restrictions of custom mediation.
To find this tool navigate to the **Mediation Setup > Ad Networks** tab.
To check if custom mediation was already integrated for any of
your app navigate to the **Mediation Setup > Mediations**:
---
## Popular Use Cases
In this section, you can find some basic use cases for Mediation Groups
**Use Case 1**
If you have a direct deal with an ad network, and you agreed
on the position of the network in the waterfall, you should use
the manual section to obtain the necessary results.
**Use Case 2:**
Sometimes ad networks cannot display your statistics for different reasons.
In this case, the positions of ad units for this ad network
will be automatically changed by our algorithm unless you put them
in the manual or eCPM sections.
**Use Case 3:**
If you are not sure which section suits you best, use the automatic priority section.
**Use Case 4:**
You can use segment targeting to conduct A/B tests of two waterfalls
for one country (by adding “country” and “part of audience”
filters in the segments).
**Use Case 5:**
To use the same mediation group for a range of countries, you can target
mediation groups to these countries instead of creating mediation groups
for each of them.
---
## Ad Revenue Attribution
Last version in PDF: [Ad Revenue AttributionDocs.pdf](/img/attachments/other/32971739/ad-revenue-attr-docs.pdf) (For May 23rd, 2019)
## **General info**
Ad Revenue attribution API is available for our customers and can be
enabled on request. Appodeal will decide regarding the enabling of this
API.
After enabling it is very easy to start working with API: Appodeal will
automatically generate a set of daily files including the information
described below.
All files are created automatically and are stored for 7 days.
You should use your API key and user ID at
[https://app.appodeal.com/user_profile/edit/api_credentials](https://app.appodeal.com/user_profile/edit/api_credentials) and use these
credentials in every API call.
There is only one endpoint that can be used to get a list of available
files.
Example of the request:
```
https://api-services.appodeal.com/api/v2/get_log_urls?api_key={your api key}&user_id={your user id}
```
The result is (in case there are files available):
```
{
log_files: [
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_04_clicks_116bc9d7e45906241a6a4.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_04_impressions_f7d8ec46adbef8442469e.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_05_clicks_fd310ab6ceac36a7f9edb.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_05_impressions_cf09dae6ba0d8efb64a26.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_06_clicks_f5de438b44378d6143118.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_06_impressions_3d939797c3bf301921e62.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_07_clicks_11135427a1befd9ce7583.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_07_impressions_66795a3cfef15031e7658.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_08_clicks_87dde4827d94498e306e1.csv",
"https://appodeal-ad-rev-attribution.s3.amazonaws.com/for_{your user id}/2017_10_08_impressions_4e22c7d6f974cb94a5ab1.csv"
],
status: 200,
message: "success"
}
```
URL format:
```
https://appodeal-ad-rev-attribution.s3.amazonaws.com/for\_{your user id}/{log date with underscore}\_{clicks or impressions log}\_{dynamic unique hash}.csv
```
- `{your user id}` - id of the user
- `{log date with underscore}` - a date for which the data is presented
- `{clicks or impressions log}` - \[clicks\|impressions\] - shows what
is contained in the file
- `{dynamic unique hash}` - the unique hash
Request to get the list of demo log files:
```
https://api-services.appodeal.com/api/v2/demo_log_files_urls?api_key={your api key}&user_id={your user id}
```
:::info
The data in output files have 5 days delay. The first data will appear
only after 5 days from the time the API was enabled!
:::
## **CSV files format**
Every file that can be downloaded contains the following information
separated with tabulation:
| Field | Description | Type |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------| ------- |
| date | Date | date |
| user | Appodeal User ID | integer |
| device | device ID (IDFA or GAID) | string |
| idfv | IDFV | string |
| app_set_id | App Set ID | string |
| ad_type | ad type `interstitial = 1, video = 2, banner = 3, native = 4, mrec = 5, rewarded_video = 6` | integer |
| app | Appodeal App ID | integer |
| bundle | App bundle (package name) | string |
| country | Country code | string |
| image | Image ID | integer |
| revenue_key | Combination of app, country and image | string |
| timestamp | record unix timestamp | integer |
| model | device model | string |
| platform | platform ID `GOOGLE = 1, AMAZON = 2, IOS = 4, TVOS = 5` | integer |
| ip | IP address | string |
| os_version | device OS version | string |
| sdk | sdk version | string |
| address | device user address | string |
| tz | timezone | string |
| lat | latitude | decimal |
| lon | longitude | decimal |
| connection | device connection to the internet | string |
| battery | battery level | string |
| kind | device kind | string |
| network | ad network name | string |
| package_version | application package version | string |
| revenue | revenue | decimal |
| total_session_impressions | **Deprecated** Total impressions from all ad types for session, only sent if ad_type is 2 or 6, otherwise 0 | integer |
| total_session_clicks | **Deprecated** Total clicks from all ad types for session | integer |
| total_session_views | **Deprecated** Total full video views from all ad types for session | integer |
| session_impressions | Impressions for ad_type for session | integer |
| session_clicks | Clicks for ad_type for session | integer |
| session_views | Full video views for ad_type for session, only sent if ad_type is 2 or 6, otherwise 0 | integer |
| session_id | current session | string |
| session_uptime | Session Uptime | integer |
| hashed_waterfall | md5 hash of created waterfall (as long as we can, e.g. 256 bytes) | string |
| install_time | timestamp for the first open | bigint |
| appsflyer_id | Attribution ID (if any) | string |
| segment | Segment ID | integer |
| attribution_id | Attribution ID (if any) | string |
| media_source_id | Media Source ID | integer |
## **Country Codes**
| **Id** | **Code** | **Name** | **Id** | **Code** | **Name** | **Id** | **Code** | **Name** |
| ------ | -------- | ---------------------- | ------- | -------- | --------------------- | ------- | -------- | -------------------------------------------- |
| **1** | TH | Thailand | **81** | QA | Qatar | **161** | DZ | Algeria |
| **2** | JP | Japan | **82** | KW | Kuwait | **162** | GN | Guinea |
| **3** | CN | China | **83** | GP | Guadeloupe | **163** | CD | Congo |
| **4** | AU | Australia | **84** | MQ | Martinique | **164** | SZ | Swaziland |
| **5** | IN | India | **85** | GF | French Guiana | **165** | BF | Burkina Faso |
| **6** | MY | Malaysia | **86** | EG | Egypt | **166** | SL | Sierra Leone |
| **7** | KR | South Korea | **87** | DO | Dominican Republic | **167** | SO | Somalia |
| **8** | TW | Taiwan | **88** | GU | Guam | **168** | NE | Niger |
| **9** | HK | Hong Kong | **89** | PR | Puerto Rico | **169** | CF | Central African Republic |
| **10** | PH | Philippines | **90** | VI | U.S. Virgin Islands | **170** | TG | Togo |
| **11** | VN | Vietnam | **91** | NZ | New Zealand | **171** | BI | Burundi |
| **12** | FR | France | **92** | SG | Singapore | **172** | GQ | Equatorial Guinea |
| **13** | DE | Germany | **93** | ID | Indonesia | **173** | SS | South Sudan |
| **14** | IL | Israel | **94** | NP | Nepal | **174** | SN | Senegal |
| **15** | SE | Sweden | **95** | PG | Papua New Guinea | **175** | MR | Mauritania |
| **16** | IT | Italy | **96** | PK | Pakistan | **176** | DJ | Djibouti |
| **17** | NL | Netherlands | **97** | PA | Panama | **177** | KM | Comoros |
| **18** | GR | Greece | **98** | CR | Costa Rica | **178** | TN | Tunisia |
| **19** | ES | Spain | **99** | BB | Barbados | **179** | BT | Bhutan |
| **20** | AT | Austria | **100** | BS | Bahamas | **180** | UY | Uruguay |
| **21** | GB | United Kingdom | **101** | LC | Saint Lucia | **181** | GL | Greenland |
| **22** | BE | Belgium | **102** | AR | Argentina | **182** | XK | Kosovo |
| **23** | AE | United Arab Emirates | **103** | BD | Bangladesh | **183** | KY | Cayman Islands |
| **24** | RU | Russia | **104** | TK | Tokelau | **184** | JM | Jamaica |
| **25** | KZ | Kazakhstan | **105** | MO | Macao | **185** | GT | Guatemala |
| **26** | DK | Denmark | **106** | KH | Cambodia | **186** | MH | Marshall Islands |
| **27** | PT | Portugal | **107** | MV | Maldives | **187** | AW | Aruba |
| **28** | SA | Saudi Arabia | **108** | NC | New Caledonia | **188** | MC | Monaco |
| **29** | SI | Slovenia | **109** | FJ | Fiji | **189** | AI | Anguilla |
| **30** | IR | Iran | **110** | MN | Mongolia | **190** | GD | Grenada |
| **31** | NO | Norway | **111** | WF | Wallis and Futuna | **191** | PY | Paraguay |
| **32** | US | United States | **112** | AL | Albania | **192** | MS | Montserrat |
| **33** | MX | Mexico | **113** | UZ | Uzbekistan | **193** | TC | Turks and Caicos Islands |
| **34** | CA | Canada | **114** | ME | Montenegro | **194** | AG | Antigua and Barbuda |
| **35** | SY | Syria | **115** | BZ | Belize | **195** | TV | Tuvalu |
| **36** | UA | Ukraine | **116** | KP | North Korea | **196** | PF | French Polynesia |
| **37** | CY | Cyprus | **117** | VA | Vatican City | **197** | SB | Solomon Islands |
| **38** | CZ | Czech Republic | **118** | AQ | Antarctica | **198** | VU | Vanuatu |
| **39** | CH | Switzerland | **119** | PE | Peru | **199** | SR | Suriname |
| **40** | IQ | Iraq | **120** | BM | Bermuda | **200** | CK | Cook Islands |
| **41** | RO | Romania | **121** | CW | Curaçao | **201** | KI | Kiribati |
| **42** | TR | Turkey | **122** | CO | Colombia | **202** | NU | Niue |
| **43** | LB | Lebanon | **123** | VE | Venezuela | **203** | TO | Tonga |
| **44** | HU | Hungary | **124** | EC | Ecuador | **204** | TF | French Southern Territories |
| **45** | GE | Georgia | **125** | ZA | South Africa | **205** | NF | Norfolk Island |
| **46** | AF | Afghanistan | **126** | KN | Saint Kitts and Nevis | **206** | BN | Brunei |
| **47** | BR | Brazil | **127** | WS | Samoa | **207** | TM | Turkmenistan |
| **48** | AZ | Azerbaijan | **128** | BO | Bolivia | **208** | PN | Pitcairn Islands |
| **49** | PS | Palestine | **129** | GG | Guernsey | **209** | SM | San Marino |
| **50** | LT | Lithuania | **130** | MT | Malta | **210** | AX | Åland |
| **51** | OM | Oman | **131** | TJ | Tajikistan | **211** | FO | Faroe Islands |
| **52** | RS | Serbia | **132** | SC | Seychelles | **212** | SJ | Svalbard and Jan Mayen |
| **53** | SK | Slovakia | **133** | BH | Bahrain | **213** | CC | Cocos [Keeling] Islands |
| **54** | FI | Finland | **134** | NG | Nigeria | **214** | NR | Nauru |
| **55** | IS | Iceland | **135** | ZW | Zimbabwe | **215** | GS | South Georgia and the South Sandwich Islands |
| **56** | MD | Republic of Moldova | **136** | LR | Liberia | **216** | UM | U.S. Minor Outlying Islands |
| **57** | BG | Bulgaria | **137** | GH | Ghana | **217** | SX | Sint Maarten |
| **58** | MK | Macedonia | **138** | TZ | Tanzania | **218** | GW | Guinea-Bissau |
| **59** | LI | Liechtenstein | **139** | ZM | Zambia | **219** | MF | Saint Martin |
| **60** | JE | Jersey | **140** | NA | Namibia | **220** | VC | Saint Vincent and the Grenadines |
| **61** | PL | Poland | **141** | MG | Madagascar | **221** | PM | Saint Pierre and Miquelon |
| **62** | HR | Croatia | **142** | AO | Angola | **222** | BL | Saint-Barthélemy |
| **63** | BA | Bosnia and Herzegovina | **143** | CI | Ivory Coast | **223** | DM | Dominica |
| **64** | EE | Estonia | **144** | SD | Sudan | **224** | ST | São Tomé and Príncipe |
| **65** | LV | Latvia | **145** | UG | Uganda | **225** | FK | Falkland Islands |
| **66** | JO | Jordan | **146** | CM | Cameroon | **226** | MP | Northern Mariana Islands |
| **67** | KG | Kyrgyzstan | **147** | MW | Malawi | **227** | TL | East Timor |
| **68** | RE | Réunion | **148** | GA | Gabon | **228** | BQ | Bonaire |
| **69** | YT | Mayotte | **149** | ML | Mali | **229** | FM | Federated States of Micronesia |
| **70** | IE | Ireland | **150** | BJ | Benin | **230** | PW | Palau |
| **71** | IM | Isle of Man | **151** | TD | Chad | **231** | GY | Guyana |
| **72** | LY | Libya | **152** | BW | Botswana | **232** | HN | Honduras |
| **73** | LU | Luxembourg | **153** | CV | Cape Verde | **233** | NI | Nicaragua |
| **74** | AM | Armenia | **154** | RW | Rwanda | **234** | SV | El Salvador |
| **75** | VG | British Virgin Islands | **155** | CG | Republic of the Congo | **235** | AD | Andorra |
| **76** | YE | Yemen | **156** | MZ | Mozambique | **236** | MM | Myanmar [Burma] |
| **77** | BY | Belarus | **157** | GM | Gambia | **237** | LK | Sri Lanka |
| **78** | GI | Gibraltar | **158** | LS | Lesotho | **238** | HT | Haiti |
| **79** | KE | Kenya | **159** | MU | Mauritius | | | |
| **80** | CL | Chile | **160** | MA | Morocco | | | |
---
## Ad server
Ad Server allows you to cross-promote your apps or launch ad campaigns
for direct advertisers who are paying you. It supports different types
of creatives to use: **Banners**, **Static Interstitials**, **MREC**, **Video**,
**Rewarded Video** and **Native**.
## 1. Creating and Editing Campaigns
### 1.1. Campaigns list
When you click on the "Ad Server" button on the left menu, you’ll see a
list of Ad Server Campaigns with some basic information:
To see and manage creative sets you already have (or to create a new
one), click on the "Creative Sets" button. Other elements on this page
are directly related to your campaigns. Columns "Name", "Owner" and
"eCPM" show you exactly what they mean. "Limitation" column shows any
frequency capping limitations in the campaign. "Schedule" column shows
start/end dates if they exist. With the button in the "Status" column,
you are able to turn on/off your campaign.
The gear icon in the "Actions" section allows you to edit a created
campaign, and the white cross icon allows you to completely delete the
campaign. Also, you can see campaign statistics for a week after by
clicking on the "Statistics" button in the menu or copy the campaign by
clicking on the "Copy campaign button". Finally, there is an option to
see all campaign changes that have been made over a whole period:
### 1.2. Creating and editing campaigns interface
The process of creating a new campaign starts when you click on the "Add
campaign" button. After that, you will see this:
All required fields are marked by the red star on the right corner of
the label. You must set these fields:
- Campaign name
- Platform
- Ad Type
- Select Apps (your applications where you will show your ads)
- Creative Set (you have to choose an existing creative set or create
a new one)
- Choose priority of your campaign (Backfill, Maximum or input eCPM
manually)
- Click URL (where the user will be redirected after click on your
ads)
You are able to set a lot of options to target or limit your campaigns
(described in the next sections).
After your campaign is created, you can launch it from the page of this
campaign:
Or launch it from the campaign’s list page by clicking on this button:
### 1.3. Sharing campaign’s access rights
Also, you can share your campaign’s creation and editing rights with
other users. To do this:
- Go to [Access
Sharing](https://www.appodeal.com/profile/sharing) page;
- Click on "Grant Access" button;
- Choose "Manage your Campaigns";
- Select the campaigns you want to share or just leave everything as
is ("Manage all of your campaigns" is the option by default).
### 1.4. Your campaign’s statistics
You can see your campaigns statistics on the
[Dashboard](https://app.appodeal.com/analytics/reports) by selecting "Ad Server Campaigns" in Filters ->
**Network (Monetization)** :
### 1.5. Audience reach
This feature allows you to estimate approximate impressions amount for
your ad campaign. Prediction is based on your apps historical
performance for the previous day. Also, the prediction doesn't use these
parameters:
- limit time between impressions for the unique device by ... minutes;
- limit impression amount for the unique device by .. session;
- never show on a device after install / click;
- campaign Start / End;
- schedule.
## 2. Creative Sets
### 2.1. Creative sets list
As mentioned before in the previous section, to see all creative sets
(or to create a new one), click the "Creative Sets" button on the
campaigns list page. You will then be redirected [here](https://www.appodeal.ru/ad_server/creative_sets/).
To edit any creative set, click on the creative thumbnail or gear icon
button in the "Actions" column or delete any set you want by clicking on
the cross icon button. Also, please know that it's not possible to
delete a creative set that is already being used in one of the
campaigns.
:::warning
Beware of editing creative sets that are already used in
some campaigns, as it will affect all of the campaigns.
:::
### 2.2. Creating and editing interface
You can create creative sets of several ad types:
- Interstitial;
- Banner;
- MREC;
- Native;
- Video;
- Rewarded Video.
Each type has its own options, restrictions and possible creative
sizes.
### 2.3. Interstitial creative sets
You can load one or several creatives as one of these sizes:
- 900 x 1600 px;
- 900 x 1400 px;
- 1120 x 1500 px;
- 320 x 480 px;
- 1600 x 900 px;
- 1560 x 750 px;
- 1500 x 1120 px;
- 480 x 320 px;
- 768 x 1024 px;
- 1536 x 2048 px;
- 1024 x 768 px;
- 2048 x 1536 px.
Possible formats are JPG and PNG images under 1MB.
Previously uploaded creatives are marked by a red check mark. To see
each creative in the box on the left side, click on the size label.
### 2.4. Banner creative sets
You can load one or several creatives as one of these sizes: 320x50,
640x100, 728x90 px. Possible formats are JPG and PNG images under 1MB.
Previously uploaded creatives marked by a check mark and are in red. To
see each creative in the box on the left side, click on the size label.
### 2.5. Interstitials and banners common options
When you load different creative interstitial or banner sets into the
waterfall on a device, the creative that’s the closest to the user's
device dimensions will be selected. Our algorithm also takes into
account the device’s screen orientation.
You can also add a creative as a manual HTML code:
This is relevant to advanced users who want to launch direct campaigns
for an advertiser who has a "JS tag", or just a simple HTML code that
loads interstitials or banners with some JS scripts.
You have several options here (required options are marked by red
asterisks):
- "Name your Creative Set": Simply the name of your creative set.
- "Use Macros": To use one of the macros listed below, which will be
replaced by some values dynamically during waterfall creation.
- "Loading Script": This checkbox tells the SDK to wait until some ads
are loaded with JS scripts inside your HTML creative.
- "Base URL": If you have URLs with a blank base (http://, https://,
etc.), these urls will be modified with the base URL from these
fields (e.g., [//appodeal.com/](http://appodeal.com/) will be replaced
with [http://appodeal.com](http:/appodeal.com/) if
you have http:// in this field).
- "HTML Code": This is the field where you should put "JS Tag" or
another HTML code you have and would like to launch on your apps.
- "Width": Width of the interstitial/banner.
- "Height": Height of the interstitial/banner.
### 2.6. MREC creative sets
MREC creative set creating/editing interface:
You are able to load only one creative with size 320x250 px. Possible
formats are JPG and PNG images under 1MB.
As in the cases of banners and interstitials, you can use your custom
HTML code as MREC creative (but without macros, “Base URL” or “Loading
Script” options):
### 2.7. Native creative sets
Native creative set creating/editing interface:
All required fields are marked by the red star. The options you have
here:
- "Name your Creative set": Just a name of your set.
- "Title": Title at the upper part of the ads not more than 25 symbols
long.
- "Description": Text in the middle of the ads not more than 100
symbols long.
- "Icon": URL to a small picture (512x512px).
- "Main Image": URL to the main picture (1200x627px).
- "Video file": URL to the video if you have one.
- "CTA Text": Label of the button.
You can see an example of how this creative will be rendered on a user's
device at the right part of the page.
Also, you have the ability to input JSON for your native creative
manually:
The example text input consists of these fields:
- "title": Title at the upper part of the ads (not more than 25
characters long).
- "description": Text in the middle of the ads (not more than 100
characters long).
- "button": Label of the button.
- "image": URL to the main picture (1200x627px).
- "icon": URL to a small picture (512x512px).
- "video_url": URL to the video (if have one).
- "click_url": where the user will be redirected to after clicking on
your ads.
### 2.8. Video and Rewarded Video creative sets
Video and Rewarded Video differ from each other only in type (rewarded
or not). The interface looks like this:
You can load these types of creatives:
- **"Video File":** .mp4 file encoded with H264 codec with 1280x720 px
dimensions under 5 Mb and 30 seconds long.
- **"Portrait End-screen Banner":** Banner image for portrait screen
orientation with 900x1600 px dimensions under 1 Mb.
- **"Landscape End-screen Banner":** Banner image for landscape screen
orientation with 1600x900 px dimensions under 1 Mb.
You can also input URLs to track several VAST 3.0 events such as: Start
Event, First Quartile Event, Midpoint Event, Third Quartile Event,
Complete Event, Creative View Event, Mute Event, Unmute Event, Pause
Event, Resume Event, Fullscreen Event. If you want to know more about
VAST 3.0, you could read [this specification](https://www.iab.com/wp-content/uploads/2015/06/VASTv3_0.pdf).
One of the variations to create a video/rewarded video creative set is
to use a VAST tag URL:
### 2.9. HTML and Vast tag macros
You can use same macros in HTML and Vast tag creatives. The list of
macros is below:
- `{%CRR%}`: The value of 'crr' param being sent by SDK. This is a
combination of mobile country code and mobile network code.
- `{%URL_ENCODED_CRR%}`: The same thing as `{%CRR%}` but URL encoded.
- `{%SDK_VERSION%}`: SDK version (iOS only).
- `{%ADVERTISING_TRACKING%}`: advertising_tracking parameter sent by
SDK.
- `{%WIDTH%}`: Device screen width.
- `{%HEIGHT%}`: Device screen height.
- `{%IDFA%}`: IDFA of a device (iOS only).
- `{%IDFA_MD5%}`: MD5 hash of device's IDFA (iOS only).
- `{%IDFA_HEX%}`: SHA1 hash of device's IDFA (iOS only).
- `{%ADVERTISING_ID%}`: Advertising ID of a device (Android only).
- `{%ADVERTISING_ID_MD5%}`: MD5 hash of device's Advertising ID (Android
only).
- `{%ADVERTISING_ID_HEX%}`: SHA1 hash of device's Advertising ID
(Android only).
- `{%CACHEBUSTER%}`: Just a random MD5 hash.
- `{%APP_ID%}`: App ID from our database.
- `{%APP_BUNDLE_ID%}`: App bundle ID.
- `{%URL_ENCODED_APP_BUNDLE_ID%}`: URL encoded app bundle ID.
- `{%APP_NAME%}`: App name.
- `{%URL_ENCODED_APP_NAME%}`: URL encoded app name.
- `{%APP_STORE_URL%}`: App store URL.
- `{%URL_ENCODED_APP_STORE_URL%}`: URL encoded app store url.
- `{%APP_VERSION%}`: App version.
- `{%URL_ENCODED_APP_VERSION%}`: URL encoded app version.
- `{%IP_ADDRESS%}`: IP address.
- `{%URL_ENCODED_IP_ADDRESS%}`: URL encoded IP address.
- `{%USER_AGENT%}`: User agent of the device.
- `{%URL_ENCODED_USER_AGENT%}`: URL encoded user agent of the device.
- `{%LATITUDE%}`: Location latitude.
- `{%LONGITUDE%}`: Location longitude.
- `{%CONNECTION_TYPE%}`: Connection type (wifi, mobile).
- `{%OS_NAME%}`: Returns "iOS" or "Android".
- `{%OS_VERSION%}`: OS version.
- `{%DEVICE_MODEL%}`: User's device model.
- `{%URL_ENCODED_DEVICE_MODEL%}`: URL encoded user's device model.
- `{%DEVICE_MANUFACTURER%}`: User's device manufacturer.
- `{%URL_ENCODED_DEVICE_MANUFACTURER%}`: URL encoded user's device
manufacturer.
- `{%COUNTRY%}`: Country code in ISO Alpha-3 format.
- `{%DEVICE_TYPE%}`: Device type as Integer. 4 - phone, 5 - tablet.
- `{%ZIP%}`: Zip or postal code based on geo data.
- `{%UTC_OFFSET%}`: Local time as the number +/- of minutes from UTC.
- `{%COPPA%}`: 0/1 flag signals whether or not the request falls under
the United States Federal Trade Commission’s regulations for the
United States Children’s Online Privacy Protection Act (“COPPA”).
- `{%LMT%}`: “Limit Ad Tracking” signal commercially endorsed, where 0 =
tracking is unrestricted, 1 = tracking must be limited per
commercial guidelines.
- `{%CITY%}`: City name.
- `{%GDPR%}`: 0/1 flag signals whether or not GDPR is applicable for the
region.
- `{%GEO_TYPE%}`: Geolocation source. 1 - GPS/Location services; 2 - IP
address.
- `{%PPI%}`: Screen size as pixels per linear inch.
- `{%PXRATIO%}`: The ratio of physical pixels to device independent
pixels (float).
### 2.10. Creative Set: adding to and removing from Campaign Form
HTML and Vast tag macros
When creating a new campaign, click on "Add new creative set" button:
After that, you can upload a new creative set by using HTML or an
existing set.
When a creative set is successfully added to a campaign you will see
some useful information:
- **CTR** - is the Click-Through Rate of this creative set from the
previous day (the number of clicks divided by the number of
impressions).
- **IR** - is the Install Rate of this creative set from the previous day
(the number of installations/conversions divided by the number of
clicks).
:::info
To see the Install Rate,check "Never show on a device after install" and
input bundle ID of tracking app into "Advertised app bundle ID" field
(this works for SDK versions greater than 2.0.0):
:::
After this configuration, installation tracking will start working (both
of your apps should have Appodeal’s SDK on the board).
## 3. Targeting Options
Targeting options consist of 4 types: GEO, Device & Platforms, and
Connection & Locale.
### 3.1. GEO Options
We should mention that geo-targeting may not be 100% accurate because
not all devices send the exact coordinates. If the app doesn't collect
device coordinates, the GeoIP will be used for targeting instead. By
default,your campaign is targeted to all countries:
GEO block provides you 3 ways to target your campaigns by location:
- to an entire country;
- to a city;
- to a street address with particular latitude and longitude.
#### a. Country targeting
When you input the country’s name into "Include places" input (e.g.
"USA") and click on the country from the dropdown list, you will see the
country as a selected GEO location:
#### b. City targeting
When you input the name of a city into "Include places" input(e.g.
"Saint-Petersburg") and click on the city from the dropdown list, you
will see this city as a selected GEO location with the "City" label:
By clicking on the "City" label you can also change the targeting type
from "City" to "Radius":
By default, selected street address or a certain point on the map has a
radius of only 1 km. You can change it by clicking on the "1 km" label
and choose another value:
### 3.2. Device and Platform
This block provides you these options:
- iOS version range;
- device type (Phone, Tablet or both);
- device model (iOS only);
- targeting by certain IDFA/Advertising IDs (You can target your
campaign to a specific IDFA. If you add several items, separate them
using commas. Maximum 1000 items).
### 3.3. Connection & Locale
This block provides you options to filter devices by their connection
type (Wi-Fi, Mobile or both of them) and choose several mobile networks
from our lists:
You can check the whole list of mobile networks and their MNN/MNC codes [here](http://mcc-mnc.com)\*\*.
## 4. Advanced options and Frequency Capping
### 4.1. Frequency Capping Block
Frequency Capping provides you with a limitation of impressions amount
for each unique device (By default there is no limitation):
You are able to set:
- maximum impressions amount per day, per hour or per week;
- time between these impressions in minutes;
- impressions limitation per user session.
### 4.2. Advanced Options
Advanced options provide you with the ability to limit an entire
campaign’s impressions:
The main difference of the limitation from this block is that you limit
the whole amount of impressions for a campaign and not for the unique
device. You also can forbid campaign showing on a device after a click
or install by selecting each checkbox. To make "Never show on a device
after install" work you have to input bundle ID of the app you promote
into the field "Advertised app bundle ID". After this configuration,
installations tracking will start working (both of your apps should have
Appodeal’s SDK integrated). Both of the "Never show ..." options work
only from SDK versions 2.0.0.
### 4.3. Campaign Start, End and Schedule
By default, the schedule is hidden and the campaign start/end are
"Immediate" and "None". This means that after you turn on your campaign,
it’ll start working immediately and it’ll only stop when you turn it off
manually. If you want to change it, you can set start and stop dates:
The schedule provides you with the option to make your campaign live
only during selected hours and days of the week. Hour and day of the
week are based on the user's device.
## 5. Campaign Prioritization
### 5.1. Prioritizing Your Campaign
In this block you can prioritize your campaign among other ad units and
control its position by setting different eCPM values:
Params you could set here are:
- "Input eCPM manually" is the eCPM of your campaign. With this eCPM,
your campaign will be entered into the waterfall (this option is
required).
- "Click Url" param is the URL where a user will be redirected after
clicking on your ads (this option is also required).
- "Deep Link" option means that you could set a link to open your app
(where the deep link is configured) on a user's device or open a
specific screen within the app. If the app is not installed, click
URL will be used. For example, you can use "myapp://" to open your
app after click on your ads or myapp://particular_location to launch directly
into a particular location within an app.
- "Advertiser pays me" means that you could set your earnings from
this campaign here, and they will be displayed in your reports, but
will not be added to your balance.
:::info
When setting "Click URL", you can use one of the
macros/tags that will be replaced with real values after your ads get
clicked:
- `{image}` "Creative ID" - ID of yours campaign creative.
- `{device}` "Device" - user's device model.
- `{os_version}` "OS Version" - user's device OS version.
- `{device_id}` "Advertising ID"/"IDFA" - Advertising ID/IDFA of user's
device.
- `{country}` "Country" - country code (US, RU, etc.).
- `{connection}` "Connection" - connection type: "wifi" or "mobile".
- `{width}` "Width" - user's device screen width.
- `{height}` "Height" - user's device screen height.
- `{click_id}` "Click ID" - ID of the click on your ads into our
database.
- `{app}` "App" - application Bundle ID.
- `{type}` "Ad Type" - ad type of you campaign with numeric value(
Interstitial: 1, Video: 2, Banner: 3, Native: 4, MREC: 5, Rewarded
Video: 6).
:::
Also, you can choose **"Backfill"** or **"Maximum"** priority. With **"Backfill"**
variant, the campaign's eCPM will be set to 0.001$ and campaign will be
put at the end of the waterfall. So it means that this campaign will be
shown only if all other ad units do can't be filled.
On the contrary, **"Maximum"** priority allows you to put your campaign on
the top of the waterfall with 20.0$ eCPM.
The last option is "Test Mode". Campaigns in "Test Mode" are always
shown on the top of the waterfall as they would have "Maximum" priority.
But they are seen only on one or several devices which adid/idfa are set
in the "Test device IDs" fields.
As you will see, the eCPM value impacts the position of your campaign in
the waterfall. Setting manual value allows you to control the amount of
traffic going to your campaign. Generally, using "Maximum" variant is
preferable. If you want to control the amount of your traffic with a top
limit, you should use "Limit impressions amount for this campaign by" in
the "Advanced Options" block.
## 6. Best practices and FAQ
### 6.1. Checking your ads before launch on a specific device
It is better to check the campaign and creative rendering before
launching it to a whole audience. You can do it by campaign targeting to
your campaign" and input your device's advertising ID/IDFA inside "Test
device IDs" fields:
Then save the campaign and launch it. When you are sure that everything
is ok, just change the prioritization mode to "Manually", "Backfill" or
"Maximum" and save the campaign again.
### 6.2. Using HTML or JS tag as a creative for your campaign
If you want to use HTML code or a "JS Tag" in your campaign, you should
check its rendering before launching the campaign. It should be possible
to render your HTML code in a browser on your computer because, in
general, it is just a simple webpage. In most cases, it loads the
javascript file which then loads your ads through some callbacks.
As mentioned above, there is a way to check the creative on a specific
device (see "Checking your ads before launch on a specific device").
What you should also do is to be sure that all of your URLs starts from
its protocol (http://, https://, etc.) or you could set it with a "Base
Url" field while creating a creative set. "Loading script" option in
most cases is also required if real ads creative will be loaded only by
JavaScript code after some requests to ads providers.
Typical HTML creative example might look like this:
All params are given only as an example, in real world they can vary.
But usually some common things stay unchanged:
- Some javascript code being loaded by "\