Usage examples

Library initialization with the extended configuration

To initialize the library with the extended startup configuration:

  1. Initialize an instance of the AppMetricaConfiguration class.
  2. Specify the configuration settings using methods of the AppMetricaConfiguration class. For example, enable logging or set a session timeout.
  3. Pass the AppMetricaConfiguration instance to the activate(with:) method of the AppMetrica class.

The parameters of the extended configuration are applied from the time of library initialization. To configure the library while the app is running, use the AppMetrica class methods.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : AnyObject]? = nil) -> Bool {
    // Creating an extended library configuration.
    if let configuration = AppMetricaConfiguration(apiKey: "API key") {
        // Setting up the configuration. For example, to enable logging.
        configuration.areLogsEnabled = true
        // ...
        // Initializing the AppMetrica SDK.
        AppMetrica.activate(with: configuration)
    }
}
  1. Initialize an instance of the AMAAppMetricaConfiguration class.
  2. Specify the configuration settings using methods of the AMAAppMetricaConfiguration class. For example, enable logging or set a session timeout.
  3. Pass the AMAAppMetricaConfiguration instance to the +activateWithConfiguration: method of the AMAAppMetrica class.

The parameters of the extended configuration are applied from the time of library initialization. To configure the library while the app is running, use the AMAAppMetrica class methods.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Creating an extended library configuration.
    AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithAPIKey:@"API key"];
    // Setting up the configuration. For example, to enable logging.
    configuration.logsEnabled = YES;
    // ...
    // Initializing the AppMetrica SDK.
    [AMAAppMetrica activateWithConfiguration:configuration];
    return YES;
}
What is an API key?

The API key is a unique app ID issued in the AppMetrica web interface at app registration. You can find it under Settings.

Sending statistics to an additional API key

Sending data to an additional API key allows you to collect own statistics for these API keys. You can use this to control access to information for other users. For example, to provide access to statistics for analysts you can duplicate sending marketing data for the additional API key. Thus they will only have access to the information they need.

To send data to an additional API key, you must use reporters. Just like for the main API key, you can set up an extended startup configuration for a reporter, send events, profile information, and data about in-app purchases. The reporter can work without the AppMetrica SDK initialization.

Step 1. (Optional) Initialize a reporter with an extended configuration

Warning

The reporter with the extended configuration should be initialized before the first call to the reporter. Otherwise, the reporter will be initialized without a configuration.

To initialize a reporter with an extended configuration:

  1. Initialize an instance of the MutableReporterConfiguration class.
  2. Specify the configuration settings using methods of the MutableReporterConfiguration class. For example, enable logging or set a session timeout.
  3. Pass the MutableReporterConfiguration instance to the activateReporter(with:) method of the AppMetrica class.
  4. This configuration is used for a reporter with the specified API key. You can set up your own configuration for each additional API key.
// Creating an extended library configuration.
// To create it, pass an API key that is different from the app's API key.
if let reporterConfiguration = MutableReporterConfiguration(apiKey: "API key") {
    // Setting up the configuration. For example, to enable logging.
    reporterConfiguration.areLogsEnabled = true
    // ...
    // Initializing a reporter.
    AppMetrica.activateReporter(with: reporterConfiguration)
}
  1. Initialize an instance of the AMAMutableReporterConfiguration class.
  2. Specify the configuration settings using methods of the AMAMutableReporterConfiguration class. For example, enable logging or set a session timeout.
  3. Pass the AMAMutableReporterConfiguration instance to the +activateReporterWithConfiguration: method of the AMAAppMetrica class.
  4. This configuration is used for a reporter with the specified API key. You can set up your own configuration for each additional API key.
// Creating an extended library configuration.
// To create it, pass an API key that is different from the app's API key.
AMAMutableReporterConfiguration *reporterConfiguration = [[AMAMutableReporterConfiguration alloc] initWithAPIKey:@"API key"];
// Setting up the configuration. For example, to enable logging.
reporterConfiguration.logsEnabled = YES;
// ...
// Initializing a reporter.
[AMAAppMetrica activateReporterWithConfiguration:[reporterConfiguration copy]];
What is an API key?

The API key is a unique app ID issued in the AppMetrica web interface at app registration. You can find it under Settings.

Step 2. Configure sending data using a reporter

To send statistics data to another API key:

  1. Use the reporter(for:) method of the AppMetrica class to get the instance that implements the AppMetricaReporting protocol.

    If the reporter was not initialized with the extended configuration, calling this method will initialize the reporter for the specified API key.

  2. Use methods of the AppMetricaReporting protocol to send events and revenue.

  3. To ensure that sessions are tracked correctly, set up sending session start and pause events for each reporter.

guard let reporter = AppMetrica.reporter(for: "API key") else {
    print("REPORT ERROR: Failed to create AppMetrica reporter")
    return // or return someDefaultValue or throw someError
}
reporter.resumeSession()
// ...
reporter.reportEvent(name: "Updates installed", onFailure: { (error) in
    print("REPORT ERROR: %@", error.localizedDescription)
})
// ...
reporter.pauseSession()
  1. Use the +reporterForAPIKey: method of the AMAAppMetrica class to get the instance that implements the AMAAppMetricaReporting protocol.

    If the reporter was not initialized with the extended configuration, calling this method will initialize the reporter for the specified API key.

  2. Use methods of the AMAAppMetricaReporting protocol to send events and revenue.

  3. To ensure that sessions are tracked correctly, set up sending session start and pause events for each reporter.

id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"API key"];
[reporter resumeSession];
// ...
[reporter reportEvent:@"Updates installed" onFailure:^(NSError *error) {
    NSLog(@"REPORT ERROR: %@", [error localizedDescription]);
}];
// ...
[reporter pauseSession];
What is an API key?

The API key is a unique app ID issued in the AppMetrica web interface at app registration. You can find it under Settings.

Tracking app crashes

Reports on app crashes are sent by default.

To disable automatic tracking, pass the configuration in which sending crashes is disabled to the crash module.

To do this, set the false value for the autoCrashTracking property of the AppMetricaCrashesConfiguration configuration.

// Creating a crashes configuration.
let configuration = AppMetricaCrashesConfiguration()
// Disabling sending the information on crashes of the application.
configuration.autoCrashTracking = false
// Set the configuration for AppMetricaCrashes.
AppMetricaCrashes.crashes().setConfiguration(configuration)

To do this, set the NO value for the autoCrashTracking property of the AMAAppMetricaCrashesConfiguration configuration.

// Creating a crashes configuration.
AMAAppMetricaCrashesConfiguration *configuration = [[AMAAppMetricaCrashesConfiguration alloc] init];
// Disabling sending the information on crashes of the application.
configuration.autoCrashTracking = NO;
// Set the configuration in AppMetricaCrashes.
[[AMAAppMetricaCrashes crashes] setConfiguration:configuration];

Determining the location

AppMetrica can determine the location of a device. Location accuracy depends on the configuration that the library uses for initialization:

  1. With the locationTracking option enabled. For iOS, the option is enabled by default.

    The location is determined with accuracy to the city. You can retrieve this information in reports and via the Logs API.

    The app requests GPS access. Battery consumption may increase.

  2. With the locationTracking option disabled.

    The location is determined by the IP address with accuracy to the country. You can retrieve this information in reports and via the Logs API.

    The app requests GPS access. Battery consumption does not increase.

    Note

    If you have enabled IP address masking, AppMetrica determines location with the accuracy to the country by the unmasked part of the IP address.

By default, the AppMetrica SDK is initialized with locationTracking enabled, but it doesn't request permission to get location data. You should implement this using methods of the CLLocationManager class.

To initialize a library with locationTracking disabled, set the locationTracking property of the AppMetricaConfiguration configuration to false.

// Creating an extended library configuration.
if let configuration = AppMetricaConfiguration(apiKey: "API key") {
    // Disabling sending information about the location of the device.
    configuration.locationTracking = false
    // Initializing the AppMetrica SDK.
    AppMetrica.activate(with: configuration)
}

To disable locationTracking after initializing the library, use the isLocationTrackingEnabled property of the AppMetrica class:

AppMetrica.isLocationTrackingEnabled = false

To initialize a library with locationTracking disabled, set the locationTracking property of the AMAAppMetricaConfiguration configuration to NO.

// Creating an extended library configuration.
AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithAPIKey:@"API key"];
// Disabling sending information about the device location.
configuration.locationTracking = NO;
// Initializing the AppMetrica SDK.
[AMAAppMetrica activateWithConfiguration:configuration];

To disable locationTracking after initializing the library, use the +setLocationTrackingEnabled: method of the AMAAppMetrica class:

AMAAppMetrica.locationTrackingEnabled = NO;

Setting device location manually

Before sending custom information about the device location, make sure that reporting is enabled.

By default, the device location is detected by the library.

To send custom device location, pass the CLLocation instance to the customLocation property of the AppMetrica class.

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    AppMetrica.customLocation = locations.last
}

To send custom device location using the startup configuration, pass the CLLocation instance to the customLocation property of the AppMetricaConfiguration configuration.

// Creating an extended library configuration.
if let configuration = AppMetricaConfiguration(apiKey: "API key") {
  // Set a custom location.
  configuration.customLocation = CLLocation(latitude: 0, longitude: 0)
  // Initializing the AppMetrica SDK.
  AppMetrica.activate(with: configuration)
}

To send custom device location, pass the CLLocation instance to the setCustomLocation method of the AMAAppMetrica class.

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation
{
    AMAAppMetrica.customLocation = newLocation;
}

To send custom device location using the startup configuration, pass the CLLocation instance to the customLocation property of the AMAAppMetricaConfiguration configuration.

// Creating an extended library configuration.
AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithAPIKey:@"API key"];
// Set a custom location.
configuration.customLocation = [[CLLocation alloc] initWithLatitude:0 longitude:0];
// Initializing the AppMetrica SDK.
[AMAAppMetrica activateWithConfiguration:configuration];

Sending a custom event

To send a custom event without nested parameters, pass the following parameters to the reportEvent(name:onFailure:) method of the AppMetrica class:

  • name: Short name or description of the event.
  • onFailure: The block the error is passed to. If you do not want to track the error, pass nil for this block.
var message = "Updates installed"
AppMetrica.reportEvent(name: message, onFailure: { (error) in
    print("DID FAIL TO REPORT EVENT: %@", message)
    print("REPORT ERROR: %@", error.localizedDescription)
})

To send a custom event without nested parameters, pass the following parameters to the +reportEvent:onFailure: method of the AMAAppMetrica class:

  • message: Short name or description of the event.
  • onFailure: The block the error is passed to. If you do not want to track the error, pass nil for this block.
NSString *message = @"Updates installed";
[AMAAppMetrica reportEvent:message onFailure:^(NSError *error) {
    NSLog(@"DID FAIL REPORT EVENT: %@", message);
    NSLog(@"REPORT ERROR: %@", [error localizedDescription]);
}];

Sending a custom event with nested parameters

To send a custom event with nested parameters, pass the following parameters to the reportEvent(name:parameters:onFailure:) method of the AppMetrica class:

  • name: Short name or description of the event.

  • parameters: Nested parameters as key-value pairs.

    The AppMetrica web interface displays up to five nesting levels for events. So if an event has six or more levels, only the top five are shown in the report. You can use the Reporting API to export up to ten levels.

  • onFailure: The block the error is passed to. If you do not want to track the error, pass nil for this block.

var message = "Updates installed"
let params = ["key1": "value1", "key2": "value2"]
AppMetrica.reportEvent(name: message, parameters: params, onFailure: { (error) in
    print("DID FAIL REPORT EVENT: %@", message)
    print("REPORT ERROR: %@", error.localizedDescription)
})

To send a custom event with nested parameters, pass the following parameters to the +reportEvent:parameters:onFailure: method of the AMAAppMetrica class:

  • message: Short name or description of the event.

  • parameters: Nested parameters as key-value pairs.

    The AppMetrica web interface displays up to five nesting levels for events. So if an event has six or more levels, only the top five are shown in the report. You can use the Reporting API to export up to ten levels.

  • onFailure: The block the error is passed to. If you do not want to track the error, pass nil for this block.

NSString *message = @"Updates installed";
NSDictionary *params = @{@"key1": @"value1", @"key2": @"value2"};
[AMAAppMetrica reportEvent:message parameters:params onFailure:^(NSError *error) {
    NSLog(@"DID FAIL REPORT EVENT: %@", message);
    NSLog(@"REPORT ERROR: %@", [error localizedDescription]);
}];

For more information about events, see Events.

Sending an event from the WebView's JavaScript code

The AppMetrica SDK lets you send client events from JavaScript code. To do this, you need to connect the AppMetricaWebKit module via the dependency management system that you're using.

Add the import:

import AppMetricaWebKit

Initialize the sending by calling the setupWebViewReporting(with:onFailure:) method:

let userController = WKUserContentController()
AppMetrica.setupWebViewReporting(with: JSController(userContentController:userController), onFailure: nil)
userController.add(myHandler, name: myScriptName)
let configuration = WKWebViewConfiguration()
configuration.userContentController = userController;
let webView = WKWebView(frame: .zero, configuration: configuration)

Add the import:

#import <AppMetricaWebKit/AppMetricaWebKit.h>

Initialize the sending by calling the +setupWebViewReporting:onFailure: method:

WKUserContentController *userController = [[WKUserContentController alloc] init];
[AMAAppMetrica setupWebViewReporting:[[AMAJSController alloc] initWithUserContentController:userController] onFailure:nil];
[userController addScriptMessageHandler:myHandler name:myScriptName];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = userController;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];

Call this method before loading any content. We recommend calling this method before creating a webview and before adding your scripts to WKUserContentController.

To send an event from JavaScript code, use the reportEvent(name, value) method in the AppMetrica interface:

function buttonClicked() {
  AppMetrica.reportEvent('Button clicked!', '{}');
}

Arguments of the reportEvent method:

  • name: A string. Can't be null or empty.
  • value: A JSON string. Can be null.

Sending an error message

To send an error message, you need to connect the AppMetricaCrashes module via the dependency management system that you're using.

To send a custom error message, add the import:

import AppMetricaCrashes

Then use the methods of the AppMetricaCrashes class and the AppMetricaCrashReporting protocol:

  • report(error:onFailure:)
  • report(error:options:onFailure:)
  • report(nserror:onFailure:)
  • report(nserror:options:onFailure:)

To send error messages, you can use the standard NSError class, the simplified AppMetricaError class, or the ErrorRepresentable protocol.

To send a custom error message, add the import:

#import <AppMetricaCrashes/AppMetricaCrashes.h>

Then use the methods of the AMAAppMetricaCrashes class and the AMAAppMetricaCrashReporting protocol:

  • -reportError:onFailure:
  • -reportError:options:onFailure:
  • -reportNSError:onFailure:
  • -reportNSError:options:onFailure:

To send error messages, you can use the standard NSError class, the simplified AMAError class, or the AMAErrorRepresentable protocol.

Example with NSError

If errors are sent using the NSError class, they're grouped by the domain domain and the code error code.

let firstError = NSError(domain: "io.appmetrica.error-a", code: 12, userInfo: [
    BacktraceErrorKey: Thread.callStackReturnAddresses,
    NSLocalizedDescriptionKey: "Error A"
])
AppMetricaCrashes.crashes().report(nserror: firstError)
NSError *firstError = [NSError errorWithDomain:@"io.appmetrica.error-a"
                                          code:12
                                      userInfo:@{
                                           AMABacktraceErrorKey: NSThread.callStackReturnAddresses,
                                           NSLocalizedDescriptionKey: @"Error A"
                                      }];
[[AMAAppMetricaCrashes crashes] reportNSError:firstError onFailure:nil];

Example with AppMetricaError

If errors are sent using the AppMetricaError class or the ErrorRepresentable protocol, they're grouped by the identifier.

let underlyingError = AppMetricaError(identifier: "Underlying AMAError")
let error = AppMetricaError(
    identifier: "AMAError identifier",
    message: "Another custom message",
    parameters: [
        "foo": "bar"
    ],
    backtrace: Thread.callStackReturnAddresses,
    underlyingError: underlyingError
)
AppMetricaCrashes.crashes().report(error: error)

If errors are sent using the AMAError class or the AMAErrorRepresentable protocol, they're grouped by the identifier.

AMAError *underlyingError = [AMAError errorWithIdentifier:@"Underlying AMAError"];
AMAError *error = [AMAError errorWithIdentifier:@"AMAError identifier"
                                        message:@"Another custom message"
                                     parameters:@{ @"foo": @"bar" }
                                      backtrace:NSThread.callStackReturnAddresses
                                underlyingError:underlyingError];
[[AMAAppMetricaCrashes crashes] reportError:error onFailure:nil];

Don't use variable values as grouping IDs. Otherwise, the number of groups increases and it becomes difficult to analyze them.

Sending ProfileId

If you don't configure ProfileId sending in the SDK, the web interface displays the appmetrica_device_id value.

To send the ProfileId, use the userProfileID property of the AppMetrica class.

AppMetrica.userProfileID = "id"

To send the ProfileId, use the +setUserProfileID: method of the AMAAppMetrica class.

AMAAppMetrica.userProfileID = @"id";

Sending profile attributes

To send profile attributes, pass the following parameters to the reportUserProfile(_:onFailure:) method of the AppMetrica class:

  • userProfile: The UserProfile instance that contains an array of attribute updates. Profile attributes are created by methods of the ProfileAttribute class.
  • onFailure: The block the error is passed to. If you do not want to track the error, pass nil for this block.
let profile = MutableUserProfile()
// Updating a single user profile attribute.
let timeLeftAttribute = ProfileAttribute.customCounter("time_left")
profile.apply(timeLeftAttribute.withDelta(-4.42))
// Updating multiple attributes.
profile.apply(from: [
    // Updating predefined attributes.
    ProfileAttribute.name().withValue("John"),
    ProfileAttribute.gender().withValue(GenderType.male),
    ProfileAttribute.birthDate().withAge(24),
    ProfileAttribute.notificationsEnabled().withValue(false),
    // Updating custom attributes.
    ProfileAttribute.customString("born_in").withValueIfUndefined("Moscow"),
    ProfileAttribute.customString("address").withValueReset(),
    ProfileAttribute.customNumber("age").withValue(24),
    ProfileAttribute.customCounter("logins_count").withDelta(1),
    ProfileAttribute.customBool("has_premium").withValue(true)
])
// ProfieID is set using the method of the AppMetrica class.
AppMetrica.userProfileID = "id"

// Sending profile attributes.
AppMetrica.reportUserProfile(profile, onFailure: { (error) in
    print("REPORT ERROR: %@", error.localizedDescription)
})

To send profile attributes, pass the following parameters to the +reportUserProfile:onFailure: method of the AMAAppMetrica class:

  • userProfile: The AMAUserProfile instance that contains an array of attribute updates. To create profile attributes, use methods of the AMAProfileAttribute class.
  • onFailure: The block the error is passed to. If you do not want to track the error, pass nil for this block.
AMAMutableUserProfile *profile = [[AMAMutableUserProfile alloc] init];
// Updating a single user profile attribute.
id<AMACustomCounterAttribute> timeLeftAttribute = [AMAProfileAttribute customCounter:@"time_left"];
[profile apply:[timeLeftAttribute withDelta:-4.42]];
// Updating multiple attributes.
[profile applyFromArray:@[
    // Updating predefined attributes.
    [[AMAProfileAttribute name] withValue:@"John"],
    [[AMAProfileAttribute gender] withValue:AMAGenderTypeMale],
    [[AMAProfileAttribute birthDate] withAge:24],
    [[AMAProfileAttribute notificationsEnabled] withValue:NO],
    // Updating custom attributes.
    [[AMAProfileAttribute customString:@"born_in"] withValueIfUndefined:@"Moscow"],
    [[AMAProfileAttribute customString:@"address"] withValueReset],
    [[AMAProfileAttribute customNumber:@"age"] withValue:24],
    [[AMAProfileAttribute customCounter:@"logins_count"] withDelta:1],
    [[AMAProfileAttribute customBool:@"has_premium"] withValue:YES]
]];
// ProfieID is set using the method of the AMAAppMetrica class.
[AMAAppMetrica setUserProfileID:@"id"];

// Sending profile attributes.
[AMAAppMetrica reportUserProfile:[profile copy] onFailure:^(NSError *error) {
    NSLog(@"Error: %@", error);
}];

Sending E-commerce events

AppMetrica doesn't let you segment E-commerce events into test and non-test events. If you use the main API key for debugging purchases, the test events are included in general statistics. Therefore, to debug E-commerce event sending, use a reporter to send statistics to the additional API key.

Step 1. Configure sending E-commerce events to the test API key

For different user actions, there are appropriate types of E-commerce events. To create a specific event type, use the appropriate ECommerce class method.

The examples below show how to send specific types of events:

Opening a page
// Creating a screen object.
let screen = ECommerceScreen(
        name: "ProductCardScreen",
        categoryComponents: ["Promos", "Hot deal"],
        searchQuery: "danissimo maple syrup",
        payload: ["full_screen": "true"]
)
// Sending an e-commerce event.
if let reporter = AppMetrica.reporter(for: "Testing API key") {
   reporter.reportECommerce(.showScreenEvent(screen: screen))
}
// Creating a screen object.
AMAECommerceScreen *screen = [[AMAECommerceScreen alloc] initWithName:@"ProductCardScreen"
                                                   categoryComponents:@[ @"Promos", @"Hot deal" ]
                                                          searchQuery:@"danissimo maple syrup"
                                                              payload:@{ @"full_screen": @"true" }];
// Sending an e-commerce event.
id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"Testing API key"];
[reporter reportECommerce:[AMAECommerce showScreenEventWithScreen:screen] onFailure:nil];
Viewing a product card
// Creating a screen object.
let screen = ECommerceScreen(
        name: "ProductCardScreen",
        categoryComponents: ["Promos", "Hot deal"],
        searchQuery: "danissimo maple syrup",
        payload: ["full_screen": "true"]
)
// Creating an actualPrice object.
let actualPrice = ECommercePrice(
        fiat: .init(unit: "USD", value: .init(string: "4.53")),
        internalComponents: [
            .init(unit: "wood", value: .init(string: "30570000")),
            .init(unit: "iron", value: .init(string: "26.89")),
            .init(unit: "gold", value: .init(string: "5.1")),
        ]
)
// Creating a product object.
let product = ECommerceProduct(
        sku: "779213",
        name: "Danissimo curd product 5.9%, 130 g",
        categoryComponents: ["Groceries", "Dairy products", "Yogurts"],
        payload: ["full_screen": "true"],
        actualPrice: actualPrice,
        originalPrice: .init(
                fiat: .init(unit: "USD", value: .init(string: "5.78")),
                internalComponents: [
                    .init(unit: "wood", value: .init(string: "30590000")),
                    .init(unit: "iron", value: .init(string: "26.92")),
                    .init(unit: "gold", value: .init(string: "5.5")),
                ]
        ),
        promoCodes: ["BT79IYX", "UT5412EP"]
)
// Sending an e-commerce event.
if let reporter = AppMetrica.reporter(for: "Testing API key") {
   reporter.reportECommerce(.showProductCardEvent(product: product, screen: screen))
}
// Creating a screen object.
AMAECommerceScreen *screen = [[AMAECommerceScreen alloc] initWithName:@"ProductCardScreen"
                                                   categoryComponents:@[ @"Promos", @"Hot deal" ]
                                                          searchQuery:@"danissimo maple syrup"
                                                              payload:@{ @"full_screen": @"true" }];
// Creating an actualPrice object.
AMAECommerceAmount *actualFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"4.53"]];
AMAECommerceAmount *woodActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30570000"]];
AMAECommerceAmount *ironActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.89"]];
AMAECommerceAmount *goldActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.1"]];
AMAECommercePrice *actualPrice = [[AMAECommercePrice alloc] initWithFiat:actualFiat
                                                      internalComponents:@[ woodActualPrice, ironActualPrice, goldActualPrice ]];
// Creating an originalPrice object.
AMAECommerceAmount *originalFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"5.78"]];
AMAECommerceAmount *woodOriginalPrice =
         [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30590000"]];
AMAECommerceAmount *ironOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.92"]];
AMAECommerceAmount *goldOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.5"]];
AMAECommercePrice *originalPrice = [[AMAECommercePrice alloc] initWithFiat:originalFiat
                                                        internalComponents:@[ woodOriginalPrice, ironOriginalPrice, goldOriginalPrice ]];
// Creating a product object.
AMAECommerceProduct *product = [[AMAECommerceProduct alloc] initWithSKU:@"779213"
                                                                   name:@"Danissimo curd product 5.9%, 130 g"
                                                     categoryComponents:@[ @"Groceries", @"Dairy products", @"Yogurts" ]
                                                                payload:@{ @"full_screen" : @"true" }
                                                            actualPrice:actualPrice
                                                          originalPrice:originalPrice
                                                             promoCodes:@[ @"BT79IYX", @"UT5412EP" ]];
// Sending an e-commerce event.
id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"Testing API key"];
[reporter reportECommerce:[AMAECommerce showProductCardEventWithProduct:product screen:screen] onFailure:nil];
Viewing a product page
// Creating a screen object.
let screen = ECommerceScreen(
        name: "ProductCardScreen",
        categoryComponents: ["Promos", "Hot deal"],
        searchQuery: "danissimo maple syrup",
        payload: ["full_screen": "true"]
)
// Creating an actualPrice object.
let actualPrice = ECommercePrice(
        fiat: .init(unit: "USD", value: .init(string: "4.53")),
        internalComponents: [
            .init(unit: "wood", value: .init(string: "30570000")),
            .init(unit: "iron", value: .init(string: "26.89")),
            .init(unit: "gold", value: .init(string: "5.1")),
        ]
)
// Creating a product object.
let product = ECommerceProduct(
        sku: "779213",
        name: "Danissimo curd product 5.9%, 130 g",
        categoryComponents: ["Groceries", "Dairy products", "Yogurts"],
        payload: ["full_screen": "true"],
        actualPrice: actualPrice,
        originalPrice: .init(
                fiat: .init(unit: "USD", value: .init(string: "5.78")),
                internalComponents: [
                    .init(unit: "wood", value: .init(string: "30590000")),
                    .init(unit: "iron", value: .init(string: "26.92")),
                    .init(unit: "gold", value: .init(string: "5.5")),
                ]
        ),
        promoCodes: ["BT79IYX", "UT5412EP"]
)
// Creating a referrer object.
let referrer = ECommerceReferrer(type: "button", identifier: "76890", screen: screen)
// Sending an e-commerce event.
if let reporter = AppMetrica.reporter(for: "Testing API key") {
   reporter.reportECommerce(.showProductDetailsEvent(product: product, referrer: referrer))
}
// Creating a screen object.
AMAECommerceScreen *screen = [[AMAECommerceScreen alloc] initWithName:@"ProductCardScreen"
                                                   categoryComponents:@[ @"Promos", @"Hot deal" ]
                                                          searchQuery:@"danissimo maple syrup"
                                                              payload:@{ @"full_screen": @"true" }];

// Creating an actualPrice object.
AMAECommerceAmount *actualFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"4.53"]];
AMAECommerceAmount *woodActualPrice =
         [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30570000"]];
AMAECommerceAmount *ironActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.89"]];
AMAECommerceAmount *goldActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.1"]];
AMAECommercePrice *actualPrice = [[AMAECommercePrice alloc] initWithFiat:actualFiat
                                                      internalComponents:@[ woodActualPrice, ironActualPrice, goldActualPrice ]];
// Creating an originalPrice object.
AMAECommerceAmount *originalFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"5.78"]];
AMAECommerceAmount *woodOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30590000"]];
AMAECommerceAmount *ironOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.92"]];
AMAECommerceAmount *goldOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.5"]];
AMAECommercePrice *originalPrice = [[AMAECommercePrice alloc] initWithFiat:originalFiat
                                                        internalComponents:@[ woodOriginalPrice, ironOriginalPrice, goldOriginalPrice ]];
// Creating a product object.
AMAECommerceProduct *product = [[AMAECommerceProduct alloc] initWithSKU:@"779213"
                                                                   name:@"Danissimo curd product 5.9%, 130 g"
                                                      categoryComponents:@[ @"Groceries", @"Dairy products", @"Yogurts" ]
                                                               payload:@{ @"full_screen" : @"true" }
                                                            actualPrice:actualPrice
                                                          originalPrice:originalPrice
                                                             promoCodes:@[ @"BT79IYX", @"UT5412EP" ]];
// Creating a referrer object.
AMAECommerceReferrer *referrer = [[AMAECommerceReferrer alloc] initWithType:@"button"
                                                                 identifier:@"76890"
                                                                     screen:screen];
// Sending an e-commerce event.
id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"Testing API key"];
[reporter reportECommerce:[AMAECommerce showProductDetailsEventWithProduct:product referrer:referrer] onFailure:nil];
Adding or removing an item to/from the cart
// Creating a screen object.
let screen = ECommerceScreen(
        name: "ProductCardScreen",
        categoryComponents: ["Promos", "Hot deal"],
        searchQuery: "danissimo maple syrup",
        payload: ["full_screen": "true"]
)
// Creating an actualPrice object.
let actualPrice = ECommercePrice(
        fiat: .init(unit: "USD", value: .init(string: "4.53")),
        internalComponents: [
            .init(unit: "wood", value: .init(string: "30570000")),
            .init(unit: "iron", value: .init(string: "26.89")),
            .init(unit: "gold", value: .init(string: "5.1")),
        ]
)
// Creating a product object.
let product = ECommerceProduct(
        sku: "779213",
        name: "Danissimo curd product 5.9%, 130 g",
        categoryComponents: ["Groceries", "Dairy products", "Yogurts"],
        payload: ["full_screen": "true"],
        actualPrice: actualPrice,
        originalPrice: .init(
                fiat: .init(unit: "USD", value: .init(string: "5.78")),
                internalComponents: [
                    .init(unit: "wood", value: .init(string: "30590000")),
                    .init(unit: "iron", value: .init(string: "26.92")),
                    .init(unit: "gold", value: .init(string: "5.5")),
                ]
        ),
        promoCodes: ["BT79IYX", "UT5412EP"]
)
// Creating a referrer object.
let referrer = ECommerceReferrer(type: "button", identifier: "76890", screen: screen)
// Creating a cartItem object.
let addedItems = ECommerceCartItem(
        product: product,
        quantity: .init(string: "1"),
        revenue: actualPrice,
        referrer: referrer
)
// Sending an e-commerce event.
if let reporter = AppMetrica.reporter(for: "Testing API key") {
   reporter.reportECommerce(.addCartItemEvent(cartItem: addedItems))
   // Or:
   reporter.reportECommerce(.removeCartItemEvent(cartItem: addedItems))
}
// Creating a screen object.
AMAECommerceScreen *screen = [[AMAECommerceScreen alloc] initWithName:@"ProductCardScreen"
                                                   categoryComponents:@[ @"Promos", @"Hot deal" ]
                                                          searchQuery:@"danissimo maple syrup"
                                                              payload:@{ @"full_screen": @"true" }];
// Creating an actualPrice object.
AMAECommerceAmount *actualFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"4.53"]];
AMAECommerceAmount *woodActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30570000"]];
AMAECommerceAmount *ironActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.89"]];
AMAECommerceAmount *goldActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.1"]];
AMAECommercePrice *actualPrice = [[AMAECommercePrice alloc] initWithFiat:actualFiat
                                                      internalComponents:@[ woodActualPrice, ironActualPrice, goldActualPrice ]];
// Creating an originalPrice object.
AMAECommerceAmount *originalFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"5.78"]];
AMAECommerceAmount *woodOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30590000"]];
AMAECommerceAmount *ironOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.92"]];
AMAECommerceAmount *goldOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.5"]];
AMAECommercePrice *originalPrice = [[AMAECommercePrice alloc] initWithFiat:originalFiat
                                                        internalComponents:@[ woodOriginalPrice, ironOriginalPrice, goldOriginalPrice ]];
// Creating a product object.
AMAECommerceProduct *product = [[AMAECommerceProduct alloc] initWithSKU:@"779213"
                                                                   name:@"Danissimo curd product 5.9%, 130 g"
                                                     categoryComponents:@[ @"Groceries", @"Dairy products", @"Yogurts" ]
                                                                payload:@{ @"full_screen" : @"true" }
                                                            actualPrice:actualPrice
                                                          originalPrice:originalPrice
                                                             promoCodes:@[ @"BT79IYX", @"UT5412EP" ]];
// Creating a referrer object.
AMAECommerceReferrer *referrer = [[AMAECommerceReferrer alloc] initWithType:@"button"
                                                                 identifier:@"76890"
                                                                     screen:screen];
// Creating a cartItem object.
NSDecimalNumber *quantity = [NSDecimalNumber decimalNumberWithString:@"1"];
AMAECommerceCartItem *addedItems = [[AMAECommerceCartItem alloc] initWithProduct:product
                                                                        quantity:quantity
                                                                         revenue:actualPrice
                                                                        referrer:referrer];
// Sending an e-commerce event.
id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"Testing API key"];
[reporter reportECommerce:[AMAECommerce addCartItemEventWithItem:addedItems] onFailure:nil];
// Or:
[reporter reportECommerce:[AMAECommerce removeCartItemEventWithItem:addedItems] onFailure:nil];
Starting and completing a purchase
// Creating a screen object.
let screen = ECommerceScreen(
        name: "ProductCardScreen",
        categoryComponents: ["Promos", "Hot deal"],
        searchQuery: "danissimo maple syrup",
        payload: ["full_screen": "true"]
)
// Creating an actualPrice object.
let actualPrice = ECommercePrice(
        fiat: .init(unit: "USD", value: .init(string: "4.53")),
        internalComponents: [
            .init(unit: "wood", value: .init(string: "30570000")),
            .init(unit: "iron", value: .init(string: "26.89")),
            .init(unit: "gold", value: .init(string: "5.1")),
        ]
)
// Creating a product object.
let product = ECommerceProduct(
        sku: "779213",
        name: "Danissimo curd product 5.9%, 130 g",
        categoryComponents: ["Groceries", "Dairy products", "Yogurts"],
        payload: ["full_screen": "true"],
        actualPrice: actualPrice,
        originalPrice: .init(
                fiat: .init(unit: "USD", value: .init(string: "5.78")),
                internalComponents: [
                    .init(unit: "wood", value: .init(string: "30590000")),
                    .init(unit: "iron", value: .init(string: "26.92")),
                    .init(unit: "gold", value: .init(string: "5.5")),
                ]
        ),
        promoCodes: ["BT79IYX", "UT5412EP"]
)
// Creating a referrer object.
let referrer = ECommerceReferrer(type: "button", identifier: "76890", screen: screen)
// Creating a cartItem object.
let addedItems = ECommerceCartItem(
        product: product,
        quantity: .init(string: "1"),
        revenue: actualPrice,
        referrer: referrer
)
// Creating an order object.
let order = ECommerceOrder(
        identifier: "88528768",
        cartItems: [addedItems],
        payload: ["black_friday": "true"]
)
// Sending an e-commerce event.
if let reporter = AppMetrica.reporter(for: "Testing API key") {
   reporter.reportECommerce(.beginCheckoutEvent(order:order))
   reporter.reportECommerce(.purchaseEvent(order: order))
}
// Creating a screen object.
AMAECommerceScreen *screen = [[AMAECommerceScreen alloc] initWithName:@"ProductCardScreen"
                                                   categoryComponents:@[ @"Promos", @"Hot deal" ]
                                                          searchQuery:@"danissimo maple syrup"
                                                              payload:@{ @"full_screen": @"true" }];
// Creating an actualPrice object.
AMAECommerceAmount *actualFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"4.53"]];
AMAECommerceAmount *woodActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30570000"]];
AMAECommerceAmount *ironActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.89"]];
AMAECommerceAmount *goldActualPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.1"]];
AMAECommercePrice *actualPrice = [[AMAECommercePrice alloc] initWithFiat:actualFiat
                                                      internalComponents:@[ woodActualPrice, ironActualPrice, goldActualPrice ]];
// Creating an originalPrice object.
AMAECommerceAmount *originalFiat =
        [[AMAECommerceAmount alloc] initWithUnit:@"USD" value:[NSDecimalNumber decimalNumberWithString:@"5.78"]];
AMAECommerceAmount *woodOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"wood" value:[NSDecimalNumber decimalNumberWithString:@"30590000"]];
AMAECommerceAmount *ironOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"iron" value:[NSDecimalNumber decimalNumberWithString:@"26.92"]];
AMAECommerceAmount *goldOriginalPrice =
        [[AMAECommerceAmount alloc] initWithUnit:@"gold" value:[NSDecimalNumber decimalNumberWithString:@"5.5"]];
AMAECommercePrice *originalPrice = [[AMAECommercePrice alloc] initWithFiat:originalFiat
                                                        internalComponents:@[ woodOriginalPrice, ironOriginalPrice, goldOriginalPrice ]];
// Creating a product object.
AMAECommerceProduct *product = [[AMAECommerceProduct alloc] initWithSKU:@"779213"
                                                                   name:@"Danissimo curd product 5.9%, 130 g"
                                                     categoryComponents:@[ @"Groceries", @"Dairy products", @"Yogurts" ]
                                                                payload:@{ @"full_screen" : @"true" }
                                                            actualPrice:actualPrice
                                                          originalPrice:originalPrice
                                                             promoCodes:@[ @"BT79IYX", @"UT5412EP" ]];
// Creating a referrer object.
AMAECommerceReferrer *referrer = [[AMAECommerceReferrer alloc] initWithType:@"button"
                                                                 identifier:@"76890"
                                                                     screen:screen];
// Creating a cartItem object.
NSDecimalNumber *quantity = [NSDecimalNumber decimalNumberWithString:@"1"];
AMAECommerceCartItem *addedItems = [[AMAECommerceCartItem alloc] initWithProduct:product
                                                                        quantity:quantity
                                                                         revenue:actualPrice
                                                                        referrer:referrer];
// Creating an order object.
AMAECommerceOrder *order = [[AMAECommerceOrder alloc] initWithIdentifier:@"88528768"
                                                               cartItems:@[ addedItems ]
                                                                 payload:@{ @"black_friday" : @"true"}];
// Sending an e-commerce event.
id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"Testing API key"];
[reporter reportECommerce:[AMAECommerce beginCheckoutEventWithOrder:order] onFailure:nil];
[reporter reportECommerce:[AMAECommerce purchaseEventWithOrder:order] onFailure:nil];

Step 2. Check the test app report

Make in-app test purchases. After a while, check the Purchase analysis report in the AppMetrica interface. Make sure that the report shows E-commerce events.

For more information about the report, see Purchase analysis.

Step 3. Configure sending events to the main API key

After successful testing, configure sending E-commerce events to the main API key.

To send the ECommerce object to the main API key, use the reportECommerce(_:onFailure:) method of the AppMetrica class.

// ...
// Sending an e-commerce event.
AppMetrica.reportECommerce(.showScreenEvent(screen: screen))

To send the AMAECommerce instance to the main API key, use the +reportECommerce:onFailure: method of the AMAAppMetrica class.

// ...
// Sending an e-commerce event.
[AMAAppMetrica reportECommerce:[AMAECommerce showScreenEventWithScreen:screen] onFailure:nil];

Sending Revenue

AppMetrica supports validation of purchases implemented using the StoreKit library. Validation lets you filter purchases made from hacked apps. If validation is enabled and a purchase fails it, this purchase isn't shown in reports.

Note

To validate purchases, enable the validation in the settings.

Step 1. Test sending Revenue

AppMetrica doesn't let you segment between test and non-test revenue. If you use the main API key for debugging purchases, the test purchases are included in general statistics. Therefore, to debug Revenue sending, use a reporter to send statistics to the additional API key.

This section outlines the steps for sending Revenue to the additional API key:

With validation

To validate purchases on iOS, configure sending the transactionID field and receiptData where you implement the transaction completion:

  1. Initialize the MutableRevenueInfo instance.
  2. To validate a purchase, specify transactionID and receiptData. You should receive them before invoking SKPaymentQueue.default ().finishTransaction(transaction).
  3. Send the MutableRevenueInfo instance to the test API key using the AppMetricaReporting reporter. For more information about reporters, see Sending statistics to an additional API key.
 func completeTransaction(_ transaction: SKPaymentTransaction) {
     // ...
     let price = NSDecimalNumber(string: "2100.5")
     // Initializing the Revenue instance.
     let revenueInfo = MutableRevenueInfo(priceDecimal: price, currency: "BYN")
     revenueInfo.productID = "TV soundbar"
     revenueInfo.quantity = 2
     revenueInfo.payload = ["source": "AppStore"]
     // Set purchase information for validation.
     if let url = Bundle.main.appStoreReceiptURL, let data = try? Data(contentsOf: url), let transactionID = transaction.transactionIdentifier {
         revenueInfo.transactionID = transactionID
         revenueInfo.receiptData = data
     }
     // Sending the Revenue instance using reporter.
     guard let reporter = AppMetrica.reporter(for: "Testing API key") else {
        print("Failed to create AppMetrica reporter")
        return
     }
     reporter.reportRevenue(revenueInfo, onFailure: { (error) in
         print("Revenue error: \(error.localizedDescription)")
     })
     // Remove the transaction from the payment queue.
     SKPaymentQueue.default().finishTransaction(transaction)
}
  1. Initialize the AMAMutableRevenueInfo instance.
  2. To validate a purchase, specify transactionID and receiptData. The must be received before [[SKPaymentQueue defaultQueue] finishTransaction:transaction] is invoked.
  3. Send the AMAMutableRevenueInfo instance to the test API key using the AMAAppMetricaReporting reporter. For more information about reporters, see Sending statistics to an additional API key.
 - (void)completeTransaction:(SKPaymentTransaction *)transaction
 {
     // ...
     NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithString:@"2100.5"];
     // Initializing the Revenue instance.
     AMAMutableRevenueInfo *revenueInfo = [[AMAMutableRevenueInfo alloc] initWithPriceDecimal:price currency:@"BYN"];
     revenueInfo.productID = @"TV soundbar";
     revenueInfo.quantity = 2;
     revenueInfo.payload = @{ @"source": @"AppStore" };
     // Set purchase information for validation.
     revenueInfo.transactionID = transaction.transactionIdentifier;
     revenueInfo.receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
     // Sending the Revenue instance using reporter.
     id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"Testing API key"];
     [reporter reportRevenue:[revenueInfo copy] onFailure:^(NSError *error) {
         NSLog(@"Revenue error: %@", error);
     }];
     // Remove the transaction from the payment queue.
     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
 }
Without validation

To send information about a purchase without validation:

  1. Initialize the MutableRevenueInfo instance.

  2. (Optional) To group purchases by OrderID, specify it in the payload property.

    Note

    If the OrderID is not specified, AppMetrica generates the ID automatically.

  3. Send the MutableRevenueInfo instance to the test API key using the AppMetricaReporting reporter. For more information about reporters, see Sending statistics to an additional API key.

 let price = NSDecimalNumber(string: "2100.5")
 // Initializing the Revenue instance.
 let revenueInfo = MutableRevenueInfo(priceDecimal: price, currency: "BYN")
 revenueInfo.productID = "TV soundbar"
 revenueInfo.quantity = 2
 // To group purchases, set the OrderID parameter in the payload property.
 revenueInfo.payload = ["OrderID": "Identifier", "source": "AppStore"]
 // Sending the Revenue instance using reporter.
 if let reporter = AppMetrica.reporter(for: "Testing API key") {
    reporter.reportRevenue(revenueInfo, onFailure: { (error) in
        print("Revenue error: \(error.localizedDescription)")
    })
 }
  1. Initialize the AMAMutableRevenueInfo instance.

  2. (Optional) To group purchases by OrderID, specify it in the payload property.

    Note

    If the OrderID is not specified, AppMetrica generates the ID automatically.

  3. Send the AMAMutableRevenueInfo instance to the test API key using the AMAAppMetricaReporting reporter. For more information about reporters, see Sending statistics to an additional API key.

 NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithString:@"2100.5"];
 // Initializing the Revenue instance.
 AMAMutableRevenueInfo *revenueInfo = [[AMAMutableRevenueInfo alloc] initWithPriceDecimal:price currency:@"BYN"];
 revenueInfo.productID = @"TV soundbar";
 revenueInfo.quantity = 2;
 // Setting the OrderID parameter in the payload property to group purchases
 revenueInfo.payload = @{ @"OrderID": @"Identifier", @"source": @"AppStore" };
 // Sending the Revenue instance using reporter.
 id<AMAAppMetricaReporting> reporter = [AMAAppMetrica reporterForAPIKey:@"Testing API key"];
 [reporter reportRevenue:[revenueInfo copy] onFailure:^(NSError *error) {
     NSLog(@"Revenue error: %@", error);
 }];

Step 2. Configure sending Revenue to the main API key

After successful testing, configure sending Revenue to the main API key.

To send the MutableRevenueInfo instance to the main API key, use the reportRevenue(_:onFailure:) method of the AppMetrica class.

// ...
// Sending the Revenue instance.
AppMetrica.reportRevenue(revenueInfo, onFailure: { (error) in
    print("Revenue error: \(error)")
})

To send the AMAMutableRevenueInfo instance to the main API key, use the +reportRevenue:onFailure: method of the AMAAppMetrica class.

// ...
// Sending the Revenue instance.
[AMAAppMetrica reportRevenue:[revenueInfo copy] onFailure:^(NSError *error) {
   NSLog(@"Revenue error: %@", error);
}];

Sending Ad Revenue data

Step 1. Make sure that the SDK is activated

Activation example:

let configuration = AppMetricaConfiguration.init(apiKey: "API key")
AppMetrica.activate(with: configuration!)
AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithApiKey:@"API_key"];
[AMAAppMetrica activateWithConfiguration:configuration];

Step 2. Set up sending Ad Revenue data

  1. Import the required modules:

    import AppMetricaCore
    import AppLovinSDK
    
    #import <AppMetricaCore/AppMetricaCore.h>
    #import <AppLovinSDK/AppLovinSDK.h>
    
  2. Add a helper extension for MAAdFormat:

    extension MAAdFormat {
        var appMetricaAdType: AdType {
            switch self {
            case .banner: return .banner
            case .interstitial: return .interstitial
            case .rewarded: return .rewarded
            case .native: return .native
            case .mrec: return .mrec
            default: return .other
            }
        }
    }
    
    @implementation MAAdFormat (AppMetricaAdType)
    
    - (AdType)appMetricaAdType {
        switch (self) {
            case MAAdFormat.banner:
                return AdTypeBanner;
            case MAAdFormat.interstitial:
                return AdTypeInterstitial;
            case MAAdFormat.rewarded:
                return AdTypeRewarded;
            case MAAdFormat.native:
                return AdTypeNative;
            case MAAdFormat.mrec:
                return AdTypeMrec;
            default:
                return AdTypeOther;
       }
    }
    
    @end
    
  3. Implement MAAdRevenueDelegate:

    class AdRevenueReporter: NSObject, MAAdRevenueDelegate {
        func didPayRevenue(for ad: MAAd) {
            // Create a MutableAdRevenueInfo object
            let adRevenueInfo = MutableAdRevenueInfo(adRevenue: NSDecimalNumber(value: ad.revenue), currency: "USD")
    
            // Set additional properties
            adRevenueInfo.adType = ad.format.appMetricaAdType
            adRevenueInfo.adNetwork = ad.networkName
            adRevenueInfo.adUnitID = ad.adUnitIdentifier
            adRevenueInfo.adPlacementName = ad.placement
            adRevenueInfo.precision = ad.revenuePrecision
    
            // Additional information can be added to payload if needed
            adRevenueInfo.payload = [
                "adType": ad.format.label,
                "countryCode": ALSdk.shared().configuration.countryCode,
                "networkPlacement": ad.networkPlacement
            ]
    
            // Report the ad revenue to AppMetrica
            AppMetrica.reportAdRevenue(adRevenueInfo)
        }
    }
    
    
    #import <AppMetricaCore/AppMetricaCore.h>
    #import <AppLovinSDK/AppLovinSDK.h>
    
  1. Import the required modules:

    import AppMetricaCore
    import FairBidSDK
    
    #import <AppMetricaCore/AppMetricaCore.h>
    #import <FairBidSDK/FairBidSDK.h>
    
  2. Create a function for passing ad revenue data to AppMetrica:

    func reportToAppMetrica(_ impressionData: FYBImpressionData, placementId: String, adType: AdType) {
        let revenue = impressionData.netPayout.map { NSDecimalNumber(decimal: $0.decimalValue) } ?? .zero
        let currency = impressionData.currency ?? "USD"
    
        let adRevenueInfo = MutableAdRevenueInfo(adRevenue: revenue, currency: currency)
        adRevenueInfo.adType = adType
        adRevenueInfo.adNetwork = impressionData.demandSource
        adRevenueInfo.adUnitID = impressionData.creativeId
        adRevenueInfo.precision = impressionData.priceAccuracy.appMetricaPrecision
    
        AppMetrica.reportAdRevenue(adRevenueInfo) { error in
            print("Failed to report ad revenue to AppMetrica: \(error)")
        }
    }
    
    void reportToAppMetrica(FYBImpressionData *impressionData, NSString *placementId, AdType adType) {
        NSDecimalNumber *revenue = impressionData.netPayout ? [[NSDecimalNumber alloc] initWithDecimal:impressionData.netPayout.decimalValue] : [NSDecimalNumber zero];
        NSString *currency = impressionData.currency ? impressionData.currency : @"USD";
    
        MutableAdRevenueInfo *adRevenueInfo = [[MutableAdRevenueInfo alloc] initWithAdRevenue:revenue currency:currency];
        adRevenueInfo.adType = adType;
        adRevenueInfo.adNetwork = impressionData.demandSource;
        adRevenueInfo.adUnitID = impressionData.creativeId;
        adRevenueInfo.precision = [impressionData.priceAccuracy appMetricaPrecision];
    
        [AppMetrica reportAdRevenue:adRevenueInfo completion:^(NSError *error) {
            if (error) {
                NSLog(@"Failed to report ad revenue to AppMetrica: %@", error);
            }
        }];
    }
    
  3. Add an extension for converting Fyber prices to the AppMetrica format:

    extension FYBImpressionDataPriceAccuracy {
        var appMetricaPrecision: String {
            switch self {
            case .undisclosed: return "undisclosed"
            case .predicted: return "predicted"
            case .programmatic: return "programmatic"
            @unknown default: return "unknown"
            }
        }
    }
    
    @implementation NSNumber (FYBImpressionDataPriceAccuracyAppMetricaPrecision)
    
    + (NSString *)appMetricaPrecisionForFYBImpressionDataPriceAccuracy:(FYBImpressionDataPriceAccuracy)accuracy {
        switch (accuracy) {
            case FYBImpressionDataPriceAccuracyUndisclosed:
                return @"undisclosed";
            case FYBImpressionDataPriceAccuracyPredicted:
                return @"predicted";
            case FYBImpressionDataPriceAccuracyProgrammatic:
                return @"programmatic";
            default:
                return @"unknown";
        }
    }
    
    @end
    
  4. Create a function for sending Offerwall impression reports to AppMetrica:

    func reportOfferWallShowToAppMetrica(placementId: String) {
        let adRevenueInfo = MutableAdRevenueInfo(adRevenue: NSDecimalNumber.zero, currency: "USD")
        adRevenueInfo.adType = .other
        adRevenueInfo.adUnitID = placementId
    
        AppMetrica.reportAdRevenue(adRevenueInfo) { error in
            print("Failed to report OfferWall show to AppMetrica: \(error)")
        }
    }
    
    - (void)reportOfferWallShowToAppMetricaWithPlacementId:(NSString *)placementId {
        YMMMutableAdRevenueInfo *adRevenueInfo = [[YMMMutableAdRevenueInfo alloc] initWithAdRevenue:[NSDecimalNumber zero] currency:@"USD"];
    
        adRevenueInfo.adType = YMMAdTypeOther;
        adRevenueInfo.adUnitID = placementId;
    
        [YMMYandexMetrica reportAdRevenueWithInfo:adRevenueInfo completionHandler:^(NSError * _Nullable error) {
            if (error) {
                NSLog(@"Failed to report OfferWall show to AppMetrica: %@", error);
            }
        }];
    }
    
  5. Create delegates and their corresponding methods for each ad format:

    class YourBannerDelegate: NSObject, FYBBannerDelegate {
        func bannerDidShow(_ banner: FYBBannerAdView, impressionData: FYBImpressionData) {
            reportToAppMetrica(impressionData, placementId: banner.options.placementId, adType: .banner)
        }
        // Implement other delegate methods as needed
    }
    
    class YourInterstitialDelegate: NSObject, FYBInterstitialDelegate {
        func interstitialDidShow(_ placementId: String, impressionData: FYBImpressionData) {
            reportToAppMetrica(impressionData, placementId: placementId, adType: .interstitial)
        }
        // Implement other delegate methods as needed
    }
    
    class YourRewardedDelegate: NSObject, FYBRewardedDelegate {
        func rewardedDidShow(_ placementId: String, impressionData: FYBImpressionData) {
            reportToAppMetrica(impressionData, placementId: placementId, adType: .rewarded)
        }
        // Implement other delegate methods as needed
    }
    
    class YourOfferWallDelegate: NSObject, OfferWallDelegate {
        func didShow(_ placementId: String?) {
            reportOfferWallShowToAppMetrica(placementId: placementId ?? "unknown")
        }
        // Implement other delegate methods as needed
    }
    
    // YourBannerDelegate
    @interface YourBannerDelegate : NSObject <FYBBannerDelegate>
    @end
    
    @implementation YourBannerDelegate
    - (void)bannerDidShow:(FYBBannerAdView *)banner impressionData:(FYBImpressionData *)impressionData {
        [self reportToAppMetricaWithImpressionData:impressionData placementId:banner.options.placementId adType:AdTypeBanner];
    }
    // Implement other delegate methods as needed
    @end
    
    // YourInterstitialDelegate
    @interface YourInterstitialDelegate : NSObject <FYBInterstitialDelegate>
    @end
    
    @implementation YourInterstitialDelegate
    - (void)interstitialDidShowWithPlacementId:(NSString *)placementId impressionData:(FYBImpressionData *)impressionData {
        [self reportToAppMetricaWithImpressionData:impressionData placementId:placementId adType:AdTypeInterstitial];
    }
    // Implement other delegate methods as needed
    @end
    
    // YourRewardedDelegate
    @interface YourRewardedDelegate : NSObject <FYBRewardedDelegate>
    @end
    
    @implementation YourRewardedDelegate
    - (void)rewardedDidShowWithPlacementId:(NSString *)placementId impressionData:(FYBImpressionData *)impressionData {
        [self reportToAppMetricaWithImpressionData:impressionData placementId:placementId adType:AdTypeRewarded];
    }
    // Implement other delegate methods as needed
    @end
    
    // YourOfferWallDelegate
    @interface YourOfferWallDelegate : NSObject <OfferWallDelegate>
    @end
    
    @implementation YourOfferWallDelegate
    - (void)didShowWithPlacementId:(NSString *)placementId {
        // Assuming reportOfferWallShowToAppMetrica is implemented elsewhere
        [self reportOfferWallShowToAppMetricaWithPlacementId:placementId ? placementId : @"unknown"];
    }
    // Implement other delegate methods as needed
    @end
    
  1. Import the required modules:

    import AppMetricaCore
    import GoogleMobileAds
    
    #import <AppMetricaCore/AppMetricaCore.h>
    #import <GoogleMobileAds/GoogleMobileAds.h>
    
  2. Add a helper extension for GADAdValuePrecision:

    extension GADAdValuePrecision: CustomStringConvertible {
        public var description: String {
            switch self {
            case .precise: return "precise"
            case .estimated: return "estimated"
            case .publisherProvided: return "publisher_defined"
            case .unknown: fallthrough
            @unknown default: return "unknown"
            }
        }
    }
    
    @interface GADAdValuePrecisionHelper : NSObject
    
    + (NSString *)stringFromAdValuePrecision:(GADAdValuePrecision)precision;
    
    @end
    
    @implementation GADAdValuePrecisionHelper
    
    + (NSString *)stringFromAdValuePrecision:(GADAdValuePrecision)precision {
        switch (precision) {
            case GADAdValuePrecisionPrecise:
                return @"precise";
            case GADAdValuePrecisionEstimated:
                return @"estimated";
            case GADAdValuePrecisionPublisherProvided:
                return @"publisher_defined";
            case GADAdValuePrecisionUnknown:
            default:
                return @"unknown";
        }
    }
    
    @end
    
  3. Implement the logic for loading ads and generating impression reports:

    class ViewController: UIViewController, GADFullScreenContentDelegate {
        var rewardedAd: GADRewardedAd?
    
        func requestRewardedAd() {
            GADRewardedAd.load(
                withAdUnitID: "AD_UNIT_ID", request: GADRequest()
            ) { [weak self] (ad, error) in
                if let error = error {
                    print("Rewarded ad failed to load with error: \(error.localizedDescription)")
                    return
                }
                if let ad = ad {
                    self?.rewardedAd = ad
                    self?.rewardedAd?.paidEventHandler = { adValue in
                        // Extract the impression-level ad revenue data
                        let value = adValue.value
                        let precision = adValue.precision
                        let currencyCode = adValue.currencyCode
    
                        // Get the ad unit ID
                        let adUnitId = ad.adUnitID
    
                        // Get additional response info
                        let responseInfo = ad.responseInfo
                        let loadedAdNetworkResponseInfo = responseInfo.loadedAdNetworkResponseInfo
                        let adSourceId = loadedAdNetworkResponseInfo?.adSourceID
                        let adSourceInstanceId = loadedAdNetworkResponseInfo?.adSourceInstanceID
                        let adSourceInstanceName = loadedAdNetworkResponseInfo?.adSourceInstanceName
                        let adSourceName = loadedAdNetworkResponseInfo?.adSourceName
                        let mediationGroupName = responseInfo.extrasDictionary["mediation_group_name"] as? String
                        let mediationABTestName = responseInfo.extrasDictionary["mediation_ab_test_name"] as? String
                        let mediationABTestVariant = responseInfo.extrasDictionary["mediation_ab_test_variant"] as? String
    
                        // Create AdRevenueInfo object
                        let adRevenueInfo = MutableAdRevenueInfo(adRevenue: value, currency: currencyCode)
                        adRevenueInfo.adType = .rewarded
                        adRevenueInfo.adNetwork = adSourceName ?? "AdMob"
                        adRevenueInfo.adUnitID = adUnitId
                        adRevenueInfo.adPlacementID = adSourceId
                        adRevenueInfo.precision = precision.description
    
                        // Create payload with additional information
                        var payload: [String: String] = [:]
                        if let adSourceInstanceId = adSourceInstanceId {
                            payload["ad_source_instance_id"] = adSourceInstanceId
                        }
                        if let adSourceInstanceName = adSourceInstanceName {
                            payload["ad_source_instance_name"] = adSourceInstanceName
                        }
                        if let mediationGroupName = mediationGroupName {
                           payload["mediation_group_name"] = mediationGroupName
                        }
                        if let mediationABTestName = mediationABTestName {
                           payload["mediation_ab_test_name"] = mediationABTestName
                        }
                        if let mediationABTestVariant = mediationABTestVariant {
                           payload["mediation_ab_test_variant"] = mediationABTestVariant
                        }
    
                        adRevenueInfo.payload = payload
    
                        // Report to AppMetrica
                        AppMetrica.reportAdRevenue(adRevenueInfo) { error in
                            print("Failed to report ad revenue: \(error)")
                        }
                    }
                }
             }
        }
    }
    
    @interface ViewController : UIViewController <GADFullScreenContentDelegate>
    
    @property (nonatomic, strong) GADRewardedAd *rewardedAd;
    
    @end
    
    @implementation ViewController
    
    - (void)requestRewardedAd {
        [GADRewardedAd loadWithAdUnitID:@"AD_UNIT_ID"
                                request:[GADRequest request]
                      completionHandler:^(GADRewardedAd * _Nullable rewardedAd, NSError * _Nullable error) {
    
            if (error) {
                NSLog(@"Rewarded ad failed to load with error: %@", [error localizedDescription]);
                return;
            }
    
            self.rewardedAd = rewardedAd;
            __weak typeof(self) weakSelf = self; // Avoid strong reference cycles
            self.rewardedAd.paidEventHandler = ^(GADAdValue *adValue) {
                // Extract the impression-level ad revenue data
                NSDecimalNumber *value = adValue.value;
                GADAdValuePrecision precision = adValue.precision;
                NSString *currencyCode = adValue.currencyCode;
    
                // Get the ad unit ID
                NSString *adUnitId = weakSelf.rewardedAd.adUnitID;
    
                // Create AdRevenueInfo object
                YMMMutableAdRevenueInfo *adRevenueInfo = [[YMMMutableAdRevenueInfo alloc] initWithAdRevenue:value.doubleValue currency:currencyCode];
                adRevenueInfo.adType = YMMAdTypeRewarded;
                adRevenueInfo.adNetwork = @"AdMob";
                adRevenueInfo.adUnitID = adUnitId;
                adRevenueInfo.precision = [NSString stringWithFormat:@"%ld", (long)precision];
    
                // Report to AppMetrica
                [YMMYandexMetrica reportAdRevenue:adRevenueInfo onFailure:^(NSError *error) {
                    NSLog(@"Failed to report ad revenue: %@", error);
                }];
            };
        }];
    }
    
    @end
    

Adapt the implementation for other ad formats

  1. Replace GADRewardedAd with an appropriate class:

    • Banner: GADBannerView
    • Interstitial: GADInterstitialAd
    • Rewarded interstitial: GADRewardedInterstitialAd
    • Native: GADNativeAd
  2. Change the adType when creating an AdRevenueInfo object:

    adRevenueInfo.adType = .banner       // For banner ads
    adRevenueInfo.adType = .interstitial // For interstitial and rewarded interstitial ads
    adRevenueInfo.adType = .native       // For native ads
    adRevenueInfo.adType = .rewarded     // For rewarded ads (current example)
    
    // For banner ads
    adRevenueInfo.adType = AdTypeBanner;
    
    // For interstitial and rewarded interstitial ads
    adRevenueInfo.adType = AdTypeInterstitial;
    
    // For native ads
    adRevenueInfo.adType = AdTypeNative;
    
    // For rewarded ads (current example)
    adRevenueInfo.adType = AdTypeRewarded;
    
  3. Configure the paidEventHandler at the appropriate stage of the ad loading process for each format. The implementation of the handler itself remains the same.

  4. For banner ads, add the paidEventHandler directly to the GADBannerView instance:

    bannerView.paidEventHandler = { adValue in
        // Use the same implementation as in the rewarded ad example
    }
    
    bannerView.paidEventHandler = ^(GADAdValue *adValue) {
        // Use the same implementation as in the rewarded ad example
    };
    
  5. For interstitial, rewarded interstitial, and native ads, add the paidEventHandler to the ad loading completion handler as shown in the example with rewarded ads.

Note

Don't forget to replace AD_UNIT_ID with your actual AdMob ad unit ID for each ad format you use.

Ad Revenue data from the ironSource SDK is passed to AppMetrica via the AppMetricaIronSourceAdapter:

  • The adapter must be initialized before activating ironSource.
  • AppMetrica should only be activated once during the app lifecycle. It doesn't matter if you do it before or after initializing the adapter.
  1. Import the required modules:

    import AppMetricaCore
    import AppMetricaIronSourceAdapter
    import IronSource
    
    #import <AppMetricaCore/AppMetricaCore.h>
    #import <AppMetricaIronSourceAdapter/AppMetricaIronSourceAdapter.h>
    import <IronSource/IronSource.h>
    
  2. Initialize the AppMetricaIronSourceAdapter before activating ironSource. You can usually do this in the app's initialization code. For example, in the AppDelegate structure or the main App for SwiftUI apps.

    AppMetricaIronSourceAdapter.shared.initialize()
    
    [[AppMetricaIronSourceAdapter shared] initialize];
    
  3. Configure and activate ironSource and AppMetrica:

    // Initialize IronSource
    IronSource.initWithAppKey("YOUR_IRONSOURCE_APP_KEY", adUnits: [IS_BANNER, IS_INTERSTITIAL, IS_REWARDED_VIDEO])
    
    // Activate AppMetrica
    guard let configuration = AppMetricaConfiguration(apiKey: "YOUR_APPMETRICA_API_KEY") else { return }
    AppMetrica.activate(with: configuration)
    
    // Initialize IronSource
    [IronSource initWithAppKey:@"YOUR_IRONSOURCE_APP_KEY" adUnits:@[IS_BANNER, IS_INTERSTITIAL, IS_REWARDED_VIDEO]];
    
    // Activate AppMetrica
    AppMetricaConfiguration *configuration = [[AppMetricaConfiguration alloc] initWithApiKey:@"YOUR_APPMETRICA_API_KEY"];
    if (configuration == nil) {
        return;
    }
    [AppMetrica activateWithConfiguration:configuration];
    

    Note

    The AppMetricaIronSourceAdapter automatically passes ad revenue data from ironSource to AppMetrica. You don't need to report impressions or revenue manually.

  1. Import the required modules:

    import AppMetricaCore
    import AnyThinkSDK
    import AnyThinkBanner
    import AnyThinkNative
    import AnyThinkInterstitial
    import AnyThinkRewardedVideo
    import AnyThinkMediaVideo
    
    #import <AppMetricaCore/AppMetricaCore.h>
    #import <AnyThinkSDK/AnyThinkSDK.h>
    import <AnyThinkBanner/AnyThinkBanner.h>
    import <AnyThinkNative/AnyThinkNative.h>
    import <AnyThinkInterstitial/AnyThinkInterstitial.h>
    import <AnyThinkRewardedVideo/AnyThinkRewardedVideo.h>
    import <AnyThinkMediaVideo/AnyThinkMediaVideo.h>
    
  2. Create a function for passing ad revenue data to AppMetrica:

    func reportToAppMetrica(_ extra: [AnyHashable: Any], placementID: String, adType: AdType) {
        guard let revenue = extra[kATADDelegateExtraPublisherRevenueKey] as? Double,
              let currency = extra[kATADDelegateExtraCurrencyKey] as? String else {
            print("Missing required revenue information")
            return
        }
    
        let adRevenueInfo = MutableAdRevenueInfo(adRevenue: NSDecimalNumber(value: revenue), currency: currency)
    
        adRevenueInfo.adType = adType
        adRevenueInfo.adNetwork = extra[kATADDelegateExtraNetworkNameKey] as? String
        adRevenueInfo.adUnitID = extra[kATADDelegateExtraAdunitIDKey] as? String
        adRevenueInfo.adPlacementID = placementID
        adRevenueInfo.precision = extra[kATADDelegateExtraPrecisionKey] as? String
    
        // Additional information in payload
        var payload: [String: String] = [:]
        if let networkFirmId = extra[kATADDelegateExtraNetworkFirmIdKey] as? Int {
            payload["network_firm_id"] = String(networkFirmId)
        }
        payload["adsource_id"] = extra[kATADDelegateExtraAdSourceIdKey] as? String
        // Add other relevant information to payload...
    
        adRevenueInfo.payload = payload
    
        AppMetrica.reportAdRevenue(adRevenueInfo) { error in
            print("Failed to report ad revenue to AppMetrica: \(error)")
        }
    }
    
    - (void)reportToAppMetricaWithExtra:(NSDictionary<AnyHashable, id> *)extra placementID:(NSString *)placementID adType:(AdType)adType {
        NSNumber *revenue = extra[kATADDelegateExtraPublisherRevenueKey];
        NSString *currency = extra[kATADDelegateExtraCurrencyKey];
    
        if (revenue == nil || currency == nil) {
            NSLog(@"Missing required revenue information");
            return;
        }
    
        MutableAdRevenueInfo *adRevenueInfo = [[MutableAdRevenueInfo alloc] initWithAdRevenue:[NSDecimalNumber decimalNumberWithDecimal:[revenue decimalValue]] currency:currency];
    
        adRevenueInfo.adType = adType;
        adRevenueInfo.adNetwork = extra[kATADDelegateExtraNetworkNameKey];
        adRevenueInfo.adUnitID = extra[kATADDelegateExtraAdunitIDKey];
        adRevenueInfo.adPlacementID = placementID;
        adRevenueInfo.precision = extra[kATADDelegateExtraPrecisionKey];
    
        // Additional information in payload
        NSMutableDictionary<NSString *, NSString *> *payload = [NSMutableDictionary new];
        NSNumber *networkFirmId = extra[kATADDelegateExtraNetworkFirmIdKey];
        if (networkFirmId != nil) {
            payload[@"network_firm_id"] = [networkFirmId stringValue];
        }
        payload[@"adsource_id"] = extra[kATADDelegateExtraAdSourceIdKey];
        // Add other relevant information to payload...
    
        adRevenueInfo.payload = payload;
    
        [AppMetrica reportAdRevenue:adRevenueInfo completion:^(NSError * _Nullable error) {
            if (error) {
                NSLog(@"Failed to report ad revenue to AppMetrica: %@", error);
            }
        }];
    }
    
  3. Implement the corresponding delegate methods for each ad format:

    class YourBannerDelegate: NSObject, ATBannerDelegate {
        func bannerView(_ bannerView: ATBannerView, didShowAdWithPlacementID placementID: String, extra: [AnyHashable : Any]) {
            reportToAppMetrica(extra, placementID: placementID, adType: .banner)
        }
        // Implement other delegate methods as needed
    }
    
    class YourNativeDelegate: NSObject, ATNativeADDelegate {
        func didShowNativeAd(in adView: ATNativeADView, placementID: String, extra: [AnyHashable : Any]) {
            reportToAppMetrica(extra, placementID: placementID, adType: .native)
        }
        // Implement other delegate methods as needed
    }
    
    class YourInterstitialDelegate: NSObject, ATInterstitialDelegate {
        func interstitialDidShow(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
            reportToAppMetrica(extra, placementID: placementID, adType: .interstitial)
        }
        // Implement other delegate methods as needed
    }
    
    class YourRewardedVideoDelegate: NSObject, ATRewardedVideoDelegate {
        func rewardedVideoDidShow(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
            reportToAppMetrica(extra, placementID: placementID, adType: .rewarded)
        }
        // Implement other delegate methods as needed
    }
    
    class YourSplashDelegate: NSObject, ATSplashDelegate {
        func splashDidShow(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
            reportToAppMetrica(extra, placementID: placementID, adType: .other)
        }
        // Implement other delegate methods as needed
    }
    
    class YourMediaVideoDelegate: NSObject, ATMediaVideoDelegate {
        func mediaVideoDidStartPlaying(forPlacementID placementID: String, extra: [AnyHashable : Any]) {
            reportToAppMetrica(extra, placementID: placementID, adType: .other)  media video
        }
        // Implement other delegate methods as needed
    }
    
    #import <AppMetricaCore/AppMetricaCore.h>
    #import <AnyThinkSDK/AnyThinkSDK.h>
    import <AnyThinkBanner/AnyThinkBanner.h>
    import <AnyThinkNative/AnyThinkNative.h>
    import <AnyThinkInterstitial/AnyThinkInterstitial.h>
    import <AnyThinkRewardedVideo/AnyThinkRewardedVideo.h>
    import <AnyThinkMediaVideo/AnyThinkMediaVideo.h>
    
  1. Initialize the MutableAdRevenueInfo instance.

  2. Send the MutableAdRevenueInfo instance to the main API key using the +reportAdRevenue:onFailure: method of the AppMetrica class.

    AppMetrica.report(adRevenue: adRevenueInfo) { error in
        print("AdRevenue error: \(error)")
    }
    
    [AMAAppMetrica reportAdRevenue:[adRevenueInfo copy] onFailure:^(NSError *error) {
        NSLog(@"AdRevenue error: %@", error);
    }];
    
    

Step 3. Make sure that the ad revenue data is included in the reports

  1. View ads in the app.

  2. Make sure that the Revenue report shows the same number of ad revenue events as the number of ad views.

Setting the session timeout

By default, the session timeout is 10 seconds. This is the minimum acceptable value for the sessionTimeout property.

To change the timeout length, pass the value in seconds to the sessionTimeout property of the library configuration AppMetricaConfiguration.

// Creating an extended library configuration.
if let configuration = AppMetricaConfiguration(apiKey: "API key") {
    // Setting the session timeout.
    configuration.sessionTimeout = 15
    // Initializing the AppMetrica SDK.
    AppMetrica.activate(with: configuration)
}

To change the timeout length, pass the value in seconds to the sessionTimeout property of the library configuration AMAAppMetricaConfiguration.

// Creating an extended library configuration.
AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithAPIKey:@"API key"];
// Setting the session timeout.
configuration.sessionTimeout = 15;
// Initializing the AppMetrica SDK.
[AMAAppMetrica activateWithConfiguration:configuration];

Setting the app version

By default, the app version is set in the app configuration file Info.plist (CFBundleShortVersionString).

To specify the app version from the code, pass it to the appVersion property of the AppMetricaConfiguration configuration.

// Creating an extended library configuration.
if let configuration = AppMetricaConfiguration(apiKey: "API key") {
    // Setting the app version.
    configuration.appVersion = "1.13.2"
    // Initializing the AppMetrica SDK.
    AppMetrica.activate(with: configuration)
}

To specify the app version from the code, pass it to the appVersion property of the AMAAppMetricaConfiguration configuration.

// Creating an extended library configuration.
AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithAPIKey:@"API key"];
// Setting the app version.
configuration.appVersion = @"1.13.2";
// Initializing the AppMetrica SDK.
[AMAAppMetrica activateWithConfiguration:configuration];

Warning

Starting with version 4.0 of the AppMetrica SDK for iOS, app openings via deeplinks are tracked automatically. For other versions, set up tracking manually:

  • iOS AppMetrica SDK version below 4.0. Setting up deeplink tracking for UIApplicationDelegate.
  • Setting up deeplink tracking for UISceneDelegate (AppMetrica doesn't track such app openings automatically).

You need to track app openings to correctly track remarketing campaigns.

Note

To work with Universal Links, configure them for your application.

UISceneDelegate

To manually track app openings via universal links or deeplinks, in UISceneDelegate, add the following code to the scene:willConnectToSession:options: method:

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions
{
  //Universal Link
  NSUserActivity *userActivity = [[connectionOptions.userActivities allObjects] firstObject];
  if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
    [AMAAppMetrica trackOpeningURL:context.URL];
  }
  else {
    //Deeplink
    UIOpenURLContext *context = [[connectionOptions.URLContexts allObjects] firstObject];
    if (context != nil) {
      [AMAAppMetrica trackOpeningURL:context.URL];
    }
  }
}

To manually track app openings via universal links or deeplinks in a running app, use the following code:

- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity
{
  NSURL *url = userActivity.webpageURL;
  if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
    [AMAAppMetrica trackOpeningURL:context.URL];
  }
}

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
{
  UIOpenURLContext *context = [[URLContexts allObjects] firstObject];
  if (context != nil) {
    [AMAAppMetrica trackOpeningURL:context.URL];
  }
}

To manually track app openings via universal links or deeplinks, in UISceneDelegate, add the following code to the scene(_:willConnectTo:options:) method:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Universal Link
    if let userActivity = connectionOptions.userActivities.first {
        if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
           let url = userActivity.webpageURL {
            AppMetrica.trackOpeningURL(url)
        }
    }
    
    // Deep Link
    if let context = connectionOptions.urlContexts.first {
        AppMetrica.trackOpeningURL(context.url)
    }
}

To manually track app openings via universal links or deeplinks in a running app, use the following code:

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let url = userActivity.webpageURL else { return }
    AppMetrica.trackOpeningURL(url)
}

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let context = URLContexts.first else { return }
    AppMetrica.trackOpeningURL(context.url)
}

UIApplicationDelegate

Warning

Manual setup of tracking with UIApplicationDelegate is relevant for iOS AppMetrica SDK versions below 4.0.

To manually track app openings via universal links or deeplinks, use the +trackOpeningURL: method of the AMAAppMetrica class.

To manually track app openings using deeplinks or deeplink handling in a running app, use UIApplicationDelegate and add the following modifications:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    [AMAAppMetrica trackOpeningURL:url];
    return YES;
}

// Delegate for tracking Universal links.
- (BOOL)application:(UIApplication *)application
    continueUserActivity:(NSUserActivity *)userActivity
    restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
    if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
        [AMAAppMetrica trackOpeningURL:userActivity.webpageURL];
    }
    return YES;
}

To manually track app openings via universal links or deeplinks, use the +trackOpeningURL: method of the AMAAppMetrica class.

To manually track app openings using deeplinks or deeplink handling in a running app, use UIApplicationDelegate and add the following modifications:

func application(_ application: UIApplication, openURL url: URL, sourceApplication: String?, annotation: AnyObject) -> Bool {
    AppMetrica.trackOpeningURL(url)
    return true
}

// Delegate for tracking Universal links.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
        if let url = userActivity.webpageURL {
            AppMetrica.trackOpeningURL(url)
        }
    }
    return true
}

See also How to create a remarketing campaign in AppMetrica.

Tracking new users

By default, the user is counted as a new user when the app is opened for the first time. If you connect the AppMetrica SDK to an app that already has active users, you can set up tracking new and old users to get correct statistics.

To do this, initialize the AppMetrica SDK using the AMAAppMetricaConfiguration extended startup configuration.

BOOL isFirstLaunch = NO;
// Creating an extended library configuration.
AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithAPIKey:@"API key"];
// Implement the logic for detecting whether the app is starting for the first time.
// For example, you can check for files (settings, databases, and so on),
// which the app creates on its first launch.
if (conditions) {
    isFirstLaunch = YES;
}
configuration.handleFirstActivationAsUpdate = !isFirstLaunch;
// Initializing the AppMetrica SDK.
[AMAAppMetrica activateWithConfiguration:configuration];

To do this, initialize the AppMetrica SDK using the AppMetricaConfiguration extended startup configuration.

var isFirstLaunch = false
// Creating an extended library configuration.
guard let configuration = AppMetricaConfiguration(apiKey: "API key") else {
    print("AppMetricaConfiguration initialization failed.")
    return // or return someDefaultValue or throw someError
}
// Implement the logic for detecting whether the app is starting for the first time.
// For example, you can check for files (settings, databases, and so on),
// which the app creates on its first launch.
if conditions {
    isFirstLaunch = true
}
configuration.handleFirstActivationAsUpdate = !isFirstLaunch
// Initializing the AppMetrica SDK.
AppMetrica.activate(with: configuration)

Disabling and enabling sending statistics

If you need confirmation from the user before sending statistical data, you should initialize the library with the disabled sending option.

To do this, set the NO value for the dataSendingEnabled property of the AMAAppMetricaConfiguration.

// Creating an extended library configuration.
AMAAppMetricaConfiguration *configuration = [[AMAAppMetricaConfiguration alloc] initWithAPIKey:@"API key"];
// Disabling sending statistics.
configuration.dataSendingEnabled = NO;
// Initializing the AppMetrica SDK.
[AMAAppMetrica activateWithConfiguration:configuration];

After the user has consented to sending statistics (for example, in the app settings or in the agreement on the first start of the app), you should enable sending using the +setDataSendingEnabled: method of the AMAAppMetrica class.

// Checking the status of the boolean variable. It shows the user confirmation.
if (flag) {
    // Enabling sending statistics.
    [AMAAppMetrica setDataSendingEnabled:YES];
}

To do this, set the false value for the dataSendingEnabled property of the AppMetricaConfiguration.

// Creating an extended library configuration.
if let configuration = AppMetricaConfiguration(apiKey: "API key") {
    // Disabling sending statistics.
    configuration.dataSendingEnabled = false
    // Initializing the AppMetrica SDK.
    AppMetrica.activate(with: configuration)
}

After the user has consented to sending statistics (for example, in the app settings or in the agreement on the first start of the app), you should enable sending using the setDataSendingEnabled(_:) method of the AppMetrica class.

// Checking the status of the boolean variable. It shows the user confirmation.
if flag {
    // Enabling sending statistics.
    AppMetrica.setDataSendingEnabled(true);
}

Alert example

You can use any text to inform users of statistics collection. For example:

This app uses the AppMetrica analytical service provided by YANDEX LLC, ulitsa Lva Tolstogo 16, Moscow, Russia 119021 (hereinafter referred to as Yandex) based on the Terms of Use.

AppMetrica analyzes app usage data, including the device it is running on, the installation source, calculates conversion, collects statistics of your activity for product analytics, ad campaign analysis, and optimization, as well as for troubleshooting. Information collected in this way cannot identify you.

Depersonalized information about your use of this app collected by AppMetrica tools will be transferred to Yandex and stored on Yandex’s server in the EU and the Russian Federation. Yandex will process this information to assess how you use the app, compile reports for us on our app operation, and provide other services.

Getting AppMetrica SDK IDs

To get AppMetrica SDK IDs (DeviceId, DeviceIdHash, UUID), use the requestStartupIdentifiersWithKeys() / requestStartupIdentifiers() method. To get the appmetrica_device_id, make sure to request the DeviceIdHash.

AMAIdentifiersCompletionBlock block = ^(NSDictionary<AMAStartupKey,id> * _Nullable identifiers, NSError * _Nullable error) {
    if (identifiers[kAMADeviceIDHashKey] != nil) {
        NSLog(@"deviceIDHash = %@", identifiers[kAMADeviceIDHashKey]);
    }
    if (identifiers[kAMADeviceIDKey] != nil) {
        NSLog(@"deviceID = %@", identifiers[kAMADeviceIDKey]);
    }
    if (identifiers[kAMAUUIDKey] != nil) {
        NSLog(@"uuid = %@", identifiers[kAMAUUIDKey]);
    }
};
[AMAAppMetrica requestStartupIdentifiersWithKeys:@[kAMADeviceIDHashKey, kAMADeviceIDKey, kAMAUUIDKey] completionQueue:nil completionBlock:block];
// Specifying the keys for which we need to get identifiers
let keys: [StartupKey] = [StartupKey.deviceIDKey, StartupKey.deviceIDHashKey, StartupKey.uuidKey]

// Specifying the queue on which the completion handler will be executed (for example, the main thread)
let queue = DispatchQueue.main

// Requesting IDs
AppMetrica.requestStartupIdentifiers(for: keys, on: queue) { identifiers, error in
    if let error = error {
        print("An error has occurred: \(error.localizedDescription)")
    } else if let identifiers = identifiers {
        // Processing the received IDs
        if let deviceIDHash = identifiers[StartupKey.deviceIDHashKey] as? String {
            print("AppMetrica deviceIDHash: \(deviceIDHash)")
        }
        if let deviceID = identifiers[StartupKey.deviceIDKey] as? String {
            print("AppMetrica deviceID: \(deviceID)")
        }
        if let uuid = identifiers[StartupKey.uuidKey] as? String {
            print("AppMetrica uuid: \(uuid)")
        }
    }
}  

If you didn't find the answer you were looking for, you can use the feedback form to submit your question. Please describe the problem in as much detail as possible. Attach a screenshot if possible.

Contact support