# 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: ![AdMob account](@site/static/img/attachments/android/get_started/admob_account.png) ## 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 ## 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). ![add_adjust_mmp](@site/static/img/attachments/android/services/adjust/adjust_mmp_platform.png) 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&amp;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. ![appsfyer_init_params](@site/static/img/attachments/android/services/appsflyer/appsfyer_init_params.png) 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 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>) { Log.d("Appodeal App", "onPurchaseReceived: $successPurchases"); } override fun onPurchaseFailed(reason: Throwable, failedPurchases: List>?) { Log.d("Appodeal App", "Message: ${reason.message} failedPurchases: $failedPurchases"); } }) ``` ``` 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 NSAppTransportSecurity NSAllowsArbitraryLoads ``` ### 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 GADApplicationIdentifier YOUR_ADMOB_APP_ID NSUserTrackingUsageDescription needs your advertising identifier to provide personalised advertising experience tailored to you NSCalendarsUsageDescription 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 NSUserTrackingUsageDescription This 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 NSAppTransportSecurity NSAllowsArbitraryLoads ``` 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 you NSLocationWhenInUseUsageDescription $(APP_NAME) needs your location for analytics and advertising purposes NSCalendarsUsageDescription $(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&amp;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 CFBundleURLTypes CFBundleURLSchemes // highlight-next-line fbAPP-ID FacebookAppID // highlight-next-line APP-ID FacebookClientToken // highlight-next-line CLIENT-TOKEN FacebookDisplayName // 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 NSUserTrackingUsageDescription This 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 GADApplicationIdentifier YOUR_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 "\