Basic methods

Please take a look at our Expert tips before integrating the events.

Real Payment

To track payments in a real currency, dispatch this event right after the system validates that the payment went through successfully. The event is fundamental and mandatory for all the app metrics related to monetization.

DTDAnalytics.realCurrencyPayment(orderId: "Order ID", 
                                 price: 12.5, 
                                 productId: "Product ID", 
                                 currencyCode: "USD")
ParameterTypeRestrictionsDescription

orderId

string

from 1 to 65 symbols

A unique transaction ID. Use transactionIdentifier property value in SKPaymentTransaction object in a complete transaction receipt.

currencyCode

string

precisely 3 symbols

Transaction currency (ISO 4217 standard) e.g. USD, EUR etc.

price

double

from Double.min to Double.max

The item price in the transaction currency.

productId

string

from 1 to 255 symbols

Item name. We recommend using a bundle or names in the same language.

By default (easy to change in the app’s settings) devtodev server invalidates transactions with previously-used identifiers. Besides, the server performs identifier checks by its outer appearance in order to avoid obvious fraud.

If you want to exclude fraud payments from your reports altogether, before creating ‘Real Currency Payment’ event, use devtodev anti-cheat feature.

Custom Events

If you want to track non-basic events, you can create custom events of your own. How you are going to apply them, depends solely on you.

Attention! We strongly recommend that you do not use custom event properties to transfer and store data that fits the definition of personal data!

DTDAnalytics.customEvent(eventName: "Event name")

If you want to pass custom parameters, use DTDCustomEventParameters class instance.

let parameters = DTDCustomEventParameters()
parameters.add(key: "key for string value", value: "string value")
parameters.add(key: "key for int value", value: 10)
parameters.add(key: "key for bool value", value: true)
parameters.add(key: "key for double value", value: 12.5)

DTDAnalytics.customEvent(eventName: "Event name", parameters: parameters)
ParameterTypeRestrictionsDescription

eventName

string

from 1 to 72 symbols

Custom event name.

parameters

DTDCustomEventParameters

key - from 1 to 32 symbols

value - see below

Custom event parameters.

The following data types can be passed using the DTDCustomEventParameters object:

TypeRestrictions

int

from Int64.min to Int64.max

string

from 1 to 255 symbols

bool

true/false

double

from Double.min to Double.max

devtodev supports no more than 300 custom event names in a single project. Events that exceed the limit of custom event names will be discarded. Try to integrate the tracked actions by type to the event name level, and move the characteristic tags in the parameters.

For example, if you need to track purchasing “Paper” and “Pen” items, then you don’t need to create two events with the names “Paper Purchase” and “Pen Purchase”. Create a “Purchase” event and add an “Item” parameter to it with the appropriate “Paper” or “Pen” value. This way, you can use just one event to track many items.

For a string parameter, you can use no more than 50,000 unique values ​​for the entire history of events. If the number of unique values exceeds the limit, the parameter gets locked by the system and gets discarded from the received data. Therefore, we don’t recommend using highly variable parameters like user IDs or time as string values ​​(moreover, they are automatically added to the event).

We strongly recommend that you do not change the data type passed in the same parameter. If you change the data type in a parameter, it will be duplicated with the same name, which may cause issues while processing reports.

Subscriptions

The described method is available beginning with version 2.1.0!

Tracking of subscriptions is now available for Apple App Store and Google Play only.

Please note that in order to track subscriptions, you need to do the following:

  1. Call the subscriptionPayment method (described below)

To track your income from subscriptions, you need to call the following method at the moment of the subscription purchase even if the user signed up for a trial subscription: func subscriptionPayment(transaction: SKPaymentTransaction, product: SKProduct).

For example:

extension Purchases: SKPaymentTransactionObserver {
  func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions { 
      switch transaction.transactionState {
      case .purchased:
          // Your code ...
          if let product = products?[transaction.payment.productIdentifier] {
             DTDAnalytics.subscriptionPayment(transaction: transaction, product: product)
          }
      case .restored:
          // Your code ...
      case .failed:
          // Your code ...
      default:
          // Your code ...
      }
    }
  }
}

Further user actions - renewal, unsubscription, etc. are tracked by using the data received from AppStore in the server-server format. You will need the corresponding setting for it.

Also, if you want to track changes in the status of the subscriptions purchased before devtodev SDK 2.0 integration, you need to transfer your history of previously purchased subscriptions to devtodev.

The SDK monitors the need for historical data to avoid sending out excessive queries to App Store. Use the DTDAnalytics.isRestoreTransactionHistoryRequiredmethod to check whether or not there is a need in sending out the information about the previously purchased subscriptions to devtodev. The method returns BOOL value.

An example of a purchase history query with verification of the need for it:

DTDAnalytics.isRestoreTransactionHistoryRequired { isNeedRestore in
  if isNeedRestore {
    DispatchQueue.main.async {
      SKPaymentQueue.default().restoreCompletedTransactions()
    }
  }
}

Use the DTDAnalytics.subscriptionHistory method to transfer the list of previously purchased subscriptions received from App Store.

If your project accounts users by user ID (not by device ID) and the device is used by more than one user, you need to filter the transaction history so that it will contain only those transactions that belong to the active user. Otherwise, subscriptions of all device users will be attributed to the user who was the first to launch the app after the integration of subscription tracking.

extension Purchases: SKPaymentTransactionObserver {
  func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
    // Your code ...
    let restoredTransactions = queue.transactions.filter { $0.transactionState == .restored }
    DTDAnalytics.subscriptionHistory(transactions: restoredTransactions)
  }
}

To recover the purchase history, the user should be logged in with his Apple ID. Be mindful of this before starting the recovering process.

Onboarding (tutorial)

The event allows you to track tutorial completion and identify the stages where you lose new users.

We recommend tracking the starting point (value -1) before beginning the first tutorial stage, then passing the counting number of every completed stage after its completion (integers larger than 0), and at the end, marking the moment of the last tutorial stage completion (value -2).

If your app has an option of skipping the tutorial and the user has used it, then it’s necessary to send a refusal value (value 0) only.

DTDAnalytics.tutorial(step: 1)

The method takes on the step value with an integer type.

ValueMeaning

0

The user skipped the tutorial

-1

The value defines the beginning of the tutorial

1..int

Counting number of completed tutorial stage

-2

The value defines the completion of the tutorial

Level up

This event is for games only. It is worthwhile to integrate this event into a game type project, as specified in the application settings. In projects with the “app” type, game events will not be tracked and displayed in the interface, even if they are integrated. You can verify and change the project type in Settings → General settings.

The event allows you to analyze the distribution of players over different game levels, monitor the in-game currency balance by levels. You can find more information about the right moment to use LevelUp event here.

The event should be dispatched right after the level-up. The number of the level reached is passed to the level parameter.

DTDAnalytics.levelUp(level: 2)

To monitor the average account balance of in-game currency by the end of each level, dispatch in-game currencies (resources) names and their amounts to the method signature:

let balance: [String: Int] = ["Currency name 1": 100, "Currency name 2": 10]
DTDAnalytics.levelUp(level: 2, balances: balance)

Attention! The number of tracked in-game currencies or resources (their unique names) should not exceed 30 at all times.

ParameterTypeRestrictionsDescription

level

int

From 1 to Int32.max - 1

Level reached

balances

[String:Int]

String - from 1 to 24 symbols

Int - from Int64.min to int64.max

Resources’ names and number at the time of level up

Current Balance

This event is for games only. It is worthwhile to integrate this event into a game type project, as specified in the application settings. In projects with the “app” type, game events will not be tracked and displayed in the interface, even if they are integrated. You can verify and change the project type in Settings → General settings.

To track the average balance of in-game currency disregarding the level up event, pass the list of in-game currency (resource) names and their amount to the method signature:

let balance: [String: Int] = ["Currency name 1": 100, "Currency name 2": 10]
DTDAnalytics.currentBalance(balance: balance)

Currency Accrual

This event is for games only. It is worthwhile to integrate this event into a game type project, as specified in the application settings. In projects with the “app” type, game events will not be tracked and displayed in the interface, even if they are integrated. You can verify and change the project type in Settings → General settings.

You need to dispatch the event after every game account balance refill if you want to track the average in-game currency amount acquired or earned by the players for a certain timeframe or during a level playthrough.

DTDAnalytics.currencyAccrual(currencyName: "Currency name 1", 
                             currencyAmount: 100, 
                             source: "Source name", 
                             accrualType: .earned)
ParameterTypeRestrictionsDescription

currencyName

string

from 1 to 24 symbols

In-game currency/resource name

currencyAmount

int

from 1 to Int32.max

Amount of currency in circulation

source

string

from 1 to 23 symbols

The sources of currency/resources. It can be used for breaking down income by its sources. For example, a city builder game may have some: “Rent” for the profit received from rental property, or “Bank” if the player has purchased some currency.

accrualType

DTDAccrualType (enum)

The currency/resource source type. The player can either gain resources during the game (earned) or purchase them for money (bought)

acrualType can receive one of the following values:

public enum DTDAccrualType: Int {
    case earned = 0
    case bought = 1
}

Virtual Currency Payment

This event is for games only. It is worthwhile to integrate this event into a game type project, as specified in the application settings. In projects with the “app” type, game events will not be tracked and displayed in the interface, even if they are integrated. You can verify and change the project type in Settings → General settings.

Pass this event after every purchase if you want to track in-game currency spends and items’ popularity. You can apply this event to both games and any apps with virtual currency.

DTDAnalytics.virtualCurrencyPayment(purchaseId: "Purchase ID",
                                    purchaseType: "Purchase type",
                                    purchaseAmount: 100,
                                    purchasePrice: 10,
                                    purchaseCurrency: "Purchase currency")

In case the item is sold for more than one currency/resource, you need to build a dictionary with all the names and amounts of the currencies/resources.

let resources: [String: Int] = ["Purchase currency name 1": 100, 
                                "Purchase currency name 2": 10]
DTDAnalytics.virtualCurrencyPayment(purchaseId: "Purchase ID",
                                    purchaseType: "Purchase Type",
                                    purchaseAmount: 100,
                                    resources: resources)
ParameterTypeRestrictionsDescription

purchaseId

string

from 1 to 32 symbols

A unique purchase name or ID. Make sure that the names are always in the same language otherwise they will be listed as different items.

purchaseType

string

from 1 to 96 symbols

The name of a resource group. For example, for “Wood” it can be “Construction materials”.

purchaseAmount

int

from 1 to Int32.max

The number of units of goods purchased.

purchaseCurrency

string

from 1 to 24 symbols

The name of a currency used for the purchase.

purchasePrice

int

from 1 to Int32.max

The price of the purchased item in the specified in-game currency.

Progression event

This event is for games only. It is worthwhile to integrate this event into a game type project, as specified in the application settings. In projects with the “app” type, game events will not be tracked and displayed in the interface, even if they are integrated. You can verify and change the project type in Settings → General settings.

First of all, the progression event is used in games with short (within one game session) areas/game levels, e.g. match 3 games. You can use the event to collect data on how well or how fast users complete levels, how difficult it is for them, how many resources they gained or spent, and other parameters.

There are two methods in working with progression event:

  • startProgressionEvent

  • finishProgressionEvent

When a player spawns at a location, the following method is called:

let parameters = DTDStartProgressionEventParameters()
parameters.source = "Source"
parameters.setDifficulty(difficulty: 10)

DTDAnalytics.startProgressionEvent(eventName: "Progression event name", 
                                   parameters: parameters)
ParameterTypeRestrictionsDescription

eventName

string

from 1 to 40 symbols

The name of the event. It is usually the number or the name of the area.

DTDStartProgressionEventParameters:

ParameterTypeRestrictionsDescription

source

string

from 1 to 40 symbols

The name of the previous event used for connecting events together. E.g. a previous area visited by the player.

difficulty

int

from 0 to Int32.max

An optional difficulty value which is set using the value: setDifficulty(difficulty: Int)

Once the player completes the location successfully, the following method is called:

let parameters = DTDFinishProgressionEventParameters()
parameters.successfulCompletion = true
parameters.duration = 100
parameters.spent = ["currency name 1": 1000,
                    "currency name 2": 50]
parameters.earned = ["currency name 2": 100]

DTDAnalytics.finishProgressionEvent(eventName: "Progression event name", 
                                    parameters: parameters)
ParameterTypeRestrictionsDescription

eventName

string

from 1 to 40 symbols

The name of the event. It is usually the number or the name of the area. It’s important to use the name that was specified at the area’s opening.

DTDFinishProgressionEventParameters:

ParameterTypeRestrictionsDescription

successfulCompletion

bool

true/false

The completion event result. ‘True’ if successful, ‘false’ if unsuccessful/lost.

duration

int

from 0 to Int64.max

Time in seconds taken to complete the area. If not specified, it is automatically calculated as the difference between startProgressionEvent and finishProgressionEvent method calls.

spent

[String: Int]

key - from 1 to 24 symbols

value - from 1 to Int64.max

Resources consumed during an area completion.

earned

[String: Int]

key - from 1 to 24 symbols

value - from 1 to Int64.max

Resources earned during an area completion.

The user can only be in one area at a time. When moving to another area (including nested ones), the previous location must be completed. Information about events that have not been completed by calling the finishProgressionEvent method during a game session (the finishProgressionEvent method call is not integrated, or the user uses the cached app, or the app has crashed) is not included in the statistics.

If you want to use this event to track actions that take more than one game session, you can prepare the required data and call both methods when the action is completed (successfully or unsuccessfully). For example, you can use this event to track the main questline.

Last updated