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 an 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 an 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 an 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 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 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 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. For more information, see Sending in-app purchases on iOS.

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);
}];

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
}

Learn more

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