Docs > iOS SDK

iOS SDK

#

setup

Copy link to clipboard
#

Download & Installation

Copy link to clipboard
Download

Static Files

  1. These 2 files are placed inside the Library folder.
  2. Add to your Xcode project.
  3. Select to ‘copy the files’ into your project.
  • GameAnalytics.h Required header file containing methods for GameAnalytics.
  • libGameAnalytics.a Required static library for GameAnalytics when not installing via Cocoapods.

Framework

The framework is called GameAnalytics.framework. Add this to your project.

When using a Framework it is needed to use the following when importing GameAnalytics.

#import <GameAnalytics/GameAnalytics.h>

CocoaPods Installation

Find our registered pod here. If you have not used pods before, then it is recommended to go to cocoapods.org and read more about it.

Our CocoaPod will install the Framework version of the SDK.

Update your Podfile (in your project directory) with this information using your project name and the current SDK version released.

target '[YOUR-GAME-PROJECT-NAME]' do
 pod 'GA-SDK-IOS', '~> 2.2'
end

Run this command from the terminal in the Podfile folder.

pod install

In Xcode open the .workspace file generated by the pod installation and use that going forward.

When installing through CocoaPods you should not have to add dependency Frameworks/libraries manually.

#

Configure Xcode

Copy link to clipboard

To use the SDK in Xcode it is needed to add both the SDK library and it’s dependencies.

In the “Build Phases” section add the following to “Link Binary With Libraries” for Xcode7 (using .tbd libraries).

  • AdSupport.framework
  • SystemConfiguration.framework
  • libsqlite3.tbd
  • libz.tbd
  • libGameAnalytics.a

In the “Build Settings” section add the following to “Other Linker Flags”:

  • -lC++

Swift Setup

To use the SDK in a Swift project it is needed to add a bridging header. But don’t worry – this is very easy.

Create a header file called GameAnalytics-Bridging-Header.h and add this line.

#import"GameAnalytics/GameAnalytics.h"

In the Build Settings for the Xcode Swift project locate the section called “Swift Compiler – General” and the field “Objective-C Bridging Header“.

Add a link to the GameAnalytics-Bridging-Header.h file. It should now work in Swift throughout the project.


Now you are ready to write some code!

#

initialize sdk

Copy link to clipboard
#

Using the SDK

Copy link to clipboard

Now we should be ready for adding code to activate the SDK! Follow the below steps:

  1. Configuration
  2. Initialization
  3. Adding events or changing dimensions

The configuration and initialization steps should be called inside the common iOS method applicationDidFinishLaunchingWithOptions located in the AppDelegate class.

Once step 1 & 2 is done you can add events at different parts of the game code where some relevant action is happening.

Remember to import the GameAnalytics.h file whenever you need to call the SDK.

 #import"GameAnalytics/GameAnalytics.h"
#

Configuration

Copy link to clipboard

The available configuration options are:

  • build
  • user id
  • available (allowed) custom dimensions
  • available (allowed) resource currencies
  • available (allowed) resource item types

Build

Build is used to specify the current version of your game.

Specify it using a string. It is recommended to use a 3 digit version like [major].[minor].[patch]

// Set build version
[GameAnalytics configureBuild:@"alpha 0.1.0"];

// Set build version (Swift)
GameAnalytics.configureBuild("alpha 0.1.0")

User ID

The SDK will automatically generate a user id and this is perfectly fine for almost all cases.

Sometimes it is useful to supply this user_id manually – for example if you download raw data for processing and need to match your internal user id (could be a database index on your user table) to the data collected through GameAnalytics.

Note that if you introduce this into a game that is already deployed (using the automatic id) it will start counting existing users as new users and your metrics will be affected.

Use this from the start of the app lifetime.

// Set custom user id
[GameAnalytics configureUserId:@"user1234567879"];

// Set custom user id (Swift)
GameAnalytics.configureUserId("user1234567879")

Specifying Allowed Values

For certain types it is required to define a whitelist containing possible unique values during the configuration phase.

When the SDK is being used (after initialization) only the specified values will be allowed. 20 values are allowed for each list.

Processing many unique dimension values can be taxing for our servers. A few games with poor implementation can seriously increase cost and affect stability.

Games will be blocked if they submit too many unique dimension values. We have this configuration requirement to guide users into planning what dimension values can be used.

[GameAnalytics configureAvailableResourceCurrencies:@[@"gems", @"gold"]];
[GameAnalytics configureAvailableResourceItemTypes:@[@"boost", @"lives"]];

// Set available custom dimensions
[GameAnalytics configureAvailableCustomDimensions01:@[@"ninja", @"samurai"]];
[GameAnalytics configureAvailableCustomDimensions02:@[@"whale", @"dolphin"]];
[GameAnalytics configureAvailableCustomDimensions03:@[@"horde", @"alliance"]];

Each resource currency string should only contain [A-Za-z] characters.

Enable/disable event submission

If you for GDPR purposes need to disable event submission you can call the following:

[GameAnalytics setEnabledEventSubmission:NO];

By default event submission is of course enabled. You will still receive configs if you have set any for your game even after disabling event submission.

#

Initialization

Copy link to clipboard

Call this method to initialize using the game key and game secret for your game.

// Initialize
[GameAnalytics initializeWithGameKey:@"game key" gameSecret:@"secret key"];

// Initialize (Swift)
GameAnalytics.initialize(withGameKey: "game key", gameSecret: "secret key")

Don’t have any keys yet? Head over here and register your game at the GameAnalytics website!

Once initialize is called it will start the first session and automatically handle session changes based on activity events. Read more about sessions here including manual session handling.

Below is common example of the code placed in the AppDelegate class.

Objective-C

 #import "GameAnalytics.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // ... other code from your project ...

    // Enable implementation log (disable in production)
    [GameAnalytics setEnabledInfoLog:YES];
    [GameAnalytics setEnabledVerboseLog:YES];

    // Set build version
    [GameAnalytics configureBuild:@"0.1.0"];

    // Set available virtual currencies and item types
    [GameAnalytics configureAvailableResourceCurrencies:@[@"gems", @"gold"]];
    [GameAnalytics configureAvailableResourceItemTypes:@[@"boost", @"lives"]];

    // Set available custom dimensions
    [GameAnalytics configureAvailableCustomDimensions01:@[@"ninja", @"samurai"]];
    [GameAnalytics configureAvailableCustomDimensions02:@[@"whale", @"dolphin"]];
    [GameAnalytics configureAvailableCustomDimensions03:@[@"horde", @"alliance"]];

    // Initialize
    [GameAnalytics initializeWithGameKey:@"[game key]" gameSecret:@"[secret key]"];
    
    return YES;
}

 

Swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    // ... other code from your project ...

    // Enable implementation log (disable in production)
    GameAnalytics.setEnabledInfoLog(true)
    GameAnalytics.setEnabledVerboseLog(true)

    // Set build version
    GameAnalytics.configureBuild("0.1.0")

    // Set available virtual currencies and item types
    GameAnalytics.configureAvailableResourceCurrencies(["gems", "gold"])
    GameAnalytics.configureAvailableResourceItemTypes(["boost", "lives"])

    // Set available custom dimensions
    GameAnalytics.configureAvailableCustomDimensions01(["ninja", "samurai"])
    GameAnalytics.configureAvailableCustomDimensions02(["whale", "dolphin"])
    GameAnalytics.configureAvailableCustomDimensions03(["horde", "alliance"])

    // Initialize
    GameAnalytics.initialize(withGameKey:"[game key]", gameSecret:"[secret key]")
    
    return true;
}

 

It is important at this stage that you make sure the initialisation is done correctly.

You can verify this by looking after logs tagged with GameAnalytics that confirm events are being sent. Read more about printing info and verbose logs here.

#

iMessage Games

Copy link to clipboard

You can adapt the SDK to track iMessage extension apps.

Due to the nature of extension apps the initializing phase needs to be nested under an initialize check in the viewDidLoad method in the MessagesViewController.m (or MessagesViewController.swift for Swift) script.

Objective-c

First declare this bool in MessagesViewController.h as in the following example:

@interface MessagesViewController : MSMessagesAppViewController{
    bool GA_started;
}

Now open the MessagesViewController.m script and initialize the SDK.

- (void)viewDidLoad {
    [super viewDidLoad];
    if(!GA_started){
        // Do any additional GameAnalytics setup here (custom dimensions, enable/disable logging etc).
        //..
        [GameAnalytics initializeWithGameKey:@"[game key]" gameSecret:@"[secret key]"];
    }
    GA_started = YES;
}

Remember to import the GameAnalytics.h header.

Swift

class MessagesViewController: MSMessagesAppViewController {

// other part of class

var GA_started:Bool = false

override func viewDidLoad() {
    super.viewDidLoad()
    if(!GA_started){
        // Do any additional GameAnalytics setup here (custom dimensions, enable/disable logging etc).
        //..
        GameAnalytics.initialize(withGameKey:"[game key]", gameSecret:"[secret key]");
    }
    GA_started = true;
}

// rest of class
}

The SDK tracks the activity within your extension.

If your extension contains multiple games then we suggest to track the custom activity of each game using design events.

If your extension has multiple games we suggest recording the session time locally and sending it as a value attached to a design event.

#

event tracking

Copy link to clipboard

GameAnalytics supports 6 different types of events: Business, Ad Resource, Progression, Error and Design.

To send an event, remember to include the namespace GameAnalyticsSDK:

using GameAnalyticsSDK;

If you are new to GameAnalytics and our Events, please read our event guide here. You will get the most benefit of GameAnalytics when understanding what and how to track.

#

Business

Copy link to clipboard

Business events are used to track in-game transactions using real money.

Configuration within the GameAnalytics tool is needed before receipt validation will be active. Please note that sandbox receipts will be flagged as valid. Read information about validation and requirements for different platforms here.

Business Events (with receipt)

When an in-app purchase is completed call the following method.

// Objective-C
[GameAnalytics addBusinessEventWithCurrency:@"USD" amount:999 itemType:@"Weapon" itemId:@"SwordOfFire" cartType:@"Menu" receipt:**receipt string**];

// Swift
GameAnalytics.addBusinessEvent(withCurrency: "USD" amount:999 itemType:"Weapon" itemId:"SwordOfFire" cartType:"Menu" receipt:**receipt string**)
Field Type Description Example
currency string Currency code in ISO 4217 format.http://openexchangerates.org/currencies.json USD
amount integer Amount in cents. 99 is 0.99$
itemType string The type / category of the item. GoldPacks
itemId string Specific item bought. 1000GoldPack
cartType string The game location of the purchase.Max 10 unique values. EndOfLevel
receipt base64 string The transaction receipt. Nil allowed. nil

If the receipt is nil (or is an invalid receipt) then the GA servers will register that amount as not validated.

In iOS objective-c you can get the receipt string like this:

// Objective-C
NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
if (![[NSFileManager defaultManager] fileExistsAtPath:[receiptUrl path]]) {
   NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
   NSString *receipt = [receiptData base64EncodedStringWithOptions:0];
}

// Swift
let receiptUrl = Bundle.main.appStoreReceiptURL?.path;
if (!FileManager.default.fileExists(atPath: receiptUrl!)) {
    var receiptData:Data?
    do{
        receiptData = try Data.init(contentsOf: Bundle.main.appStoreReceiptURL!)
    }
    catch{
        print("ERROR: " + error.localizedDescription)
    }
    let receipt = receiptData?.base64EncodedString(options: Data.Base64EncodingOptions.init(rawValue: 0))
}

Business Events with Auto Fetch Receipt

Using an alternative method it is possible to let the SDK retrieve the receipt automatically when called directly after a successful in-app purchase.

The code performed by the SDK is identical (almost) to the objective-c example above:

// Objective-C
[GameAnalytics addBusinessEventWithCurrency:@"USD" amount:999 itemType:@"Weapon" itemId:@"SwordOfFire" cartType:@"Menu" autoFetchReceipt: YES];

// Swift
GameAnalytics.addBusinessEvent(withCurrency: "USD" amount:999 itemType:"Weapon" itemId:"SwordOfFire" cartType:"Menu" autoFetchReceipt: true)

Price (cents) and the Currency ISO 4217 Format

The amount is an integer with the price cent value. Basically this means multiplying the retrieved price with 100. Also the currency has to conform to the ISO 4217 format.

In Objective-C you implement IAP using the SKProduct classes.

When initializing you would call the initWithProductIdentifiers method using the SKProductsRequest and get a list of products (valid and invalid). Also when completing the actual purchase you should have the SKProduct object.

It is possible to use code frameworks that handle IAP flow (like soom.la) and thereby get this information from that code. Make sure the price is in cents and that the currency strings are returned as required.

The code below demonstrates how to get price (cents) and currency from an SKProduct object called skProduct.

// Objective-C
// price in cents + local currency
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setLocale:skProduct.priceLocale];
NSString *currency = [numberFormatter currencyCode];
NSInteger priceInCents = [[skProduct.price decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithString:@"100"]] intValue];

// Swift
// price in cents + local currency
let numberFormatter = NumberFormatter.init()
numberFormatter.locale = skProduct?.priceLocale
let currency = numberFormatter.currencyCode
let priceInCents = skProduct?.price.multiplying(by: 100)

 


The GameAnalytics ad event needs to be called when certain events happen for the implemented ad sdk’s.
An ad sdk has callback methods activating code when certain things are activated (like ad show or ad clicked).

To use the ad event it is need to call the GameAnalytics SDK when these delegates are called.

The examples below describe how to implement this for the following ad-types.
– rewarded video
– interstitial
– bannner

All code examples use AdMob SDK to showcase example usage.
Other ad networks might differ in naming/structure, but the overall process should be applicable to all.

The ad SDK name argument for the ad event needs to be all lower-case with no spaces or underscore used. Here some examples of valid values to use for ad SDK names:
– admob
– unityads
– ironsource
– applovin

REWARDED VIDEOS

WHEN REQUESTING REWARDED VIDEO AD

  • At the location where a rewarded video ad is requested (download is starting) then send the following ad event as well:
if (![[GADRewardBasedVideoAd sharedInstance] isReady]) {
    // create ad request with required parameters
    GADRequest *request = [GADRequest request];
    // request new ad to be fetched
    [[GADRewardBasedVideoAd sharedInstance] loadRequest:request
                                           withAdUnitID:@""];
    // send ad event when loading
    [GameAnalytics addAdEventWithAction:GAAdActionRequest adType:GAAdTypeRewardedVideo adSdkName:@"admob" adPlacement:@""];
}

WHEN REWARDED AD IS LOADED

  • If the ad loads successfully send the following ad event when rewardBasedVideoAdDidReceiveAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd delegate is called.
  • The following example is a method for how you could handle this.
- (void)rewardBasedVideoAdDidReceiveAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
    // To enable tracking of errors related to ad network requests we recommend using a local variable to store this information.
    // Here we use a "latestRewardedVideoError" variable
    // In this method we clear the error variable as the request is successful
    latestRewardedVideoError = nil;
    // send ad event when loaded
    [GameAnalytics addAdEventWithAction:GAAdActionLoaded adType:GAAdTypeRewardedVideo adSdkName:@"admob" adPlacement:@""];
}

WHEN REWARDED AD FAILS TO LOAD

  • If the ad fails to load we track the latest when rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd didFailToLoadWithError:(NSError *)error is called.
  • The following example is a method for how you could handle this.
- (void)rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd
    didFailToLoadWithError:(NSError *)error {
    // keep track of latest error (optional, only needed if you want to track errors for "FailedShow" ad event)
    latestRewardedVideoError = error;
}

SHOWING A REWARDED VIDEO

  • When you want to show an ad, it is either available or not.
  • Call the ad event, when the ad is not available.
  • We do not call ad event on show, as we call it on ad close later.
if ([[GADRewardBasedVideoAd sharedInstance] isReady]) {
    [[GADRewardBasedVideoAd sharedInstance] presentFromRootViewController:self];
}
else
{
    // send ad event, for tracking a potential error
    // the getLatestAdError method is describer later
    [GameAnalytics addAdEventWithAction:GAAdActionFailedShow adType:GAAdTypeRewardedVideo adSdkName:@"admob" adPlacement:@"" noAdReason:getLatestAdError(latestRewardedVideoError)];

    // OR .. if you don't want to track errors
    [GameAnalytics addAdEventWithAction:GAAdActionFailedShow adType:GAAdTypeRewardedVideo adSdkName:@"admob" adPlacement:@""];
}
// TRACKING ERRORS
// if you want to track errors then we recommend defining this method
// to map the error message stored earlier in the "latestRewardedVideoError" variable.
GAAdError getLatestAdError(NSError *error)
{
    GAAdError result = GAAdErrorUnknown;

    // ! Implement a switch statement to map ad network errors to known GAAdError types.
    // possible value:
    // GAAdErrorUnknown
    // GAAdErrorOffline
    // GAAdErrorNoFill
    // GAAdErrorInternalError
    // GAAdErrorInvalidRequest
    // GAAdErrorUnableToPrecached

    return result;
}

TRACK USER TIME SPENT ON REWARDED VIDEO AD

  • If you want to track how much the user watched then start timer and keep track of current rewarded video ad when rewardBasedVideoAdDidOpen:(GADRewardBasedVideoAd *)rewardBasedVideoAd is called
  • The following example is a method for how you could handle this.
- (void)rewardBasedVideoAdDidOpen:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
    // keep track of current rewarded video ad
    currentRewardedVideoPlacement = @"";
    // start timer for this ad identifier
    [GameAnalytics startTimer:currentRewardedVideoPlacement];
}

// when application goes to background (during the display of a rewarded video ad) then the timer needs to stop.
// therefore we need to call code in viewWillDisappear:(BOOL)animated and viewWillAppear:(BOOL)animated.
- (void)viewWillDisappear:(BOOL)animated {
    if(currentRewardedVideoPlacement != nil)
    {
        [GameAnalytics pauseTimer:currentRewardedVideoPlacement];
    }
}

- (void)viewWillAppear:(BOOL)animated {
    if(currentRewardedVideoPlacement != nil)
    {
        [GameAnalytics resumeTimer:currentRewardedVideoPlacement];
    }
}

TRACKING REWARDS FROM REWARDED VIDEO ADS

  • If you want to track rewards from rewarded videos, then call an ad event in the method where rewards are registered.
  • For admob this method is called rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd didRewardUserWithReward:(GADAdReward *)reward and the following example is a method for how you could handle this.
- (void)rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd
    didRewardUserWithReward:(GADAdReward *)reward {
    // send ad event - reward recieved
    [GameAnalytics addAdEventWithAction:GAAdActionRewardReceived adType:GAAdTypeRewardedVideo adSdkName:@"admob" adPlacement:@""];
}

TRACKING REWARDED AD BEING SHOWN

  • We track the rewarded ad when it has finished being shown.
  • For admob this method is called rewardBasedVideoAdDidClose:(GADRewardBasedVideoAd *)rewardBasedVideoAd and the following example is a method for how you could handle this.
- (void)rewardBasedVideoAdDidClose:(GADRewardBasedVideoAd *)rewardBasedVideoAd {
    if(currentRewardedVideoPlacement != nil)
    {
        NSInteger elapsedTime = [GameAnalytics stopTimer:currentRewardedVideoPlacement];
        // send ad event for tracking elapsedTime
        [GameAnalytics addAdEventWithAction:GAAdActionShow adType:GAAdTypeRewardedVideo adSdkName:@"admob" adPlacement:@"" duration:elapsedTime];
        currentRewardedVideoPlacement = nil;

        // OR if you do not wish to track time

        // send ad event without tracking elapsedTime
        [GameAnalytics addAdEventWithAction:GAAdActionShow adType:GAAdTypeRewardedVideo adSdkName:@"admob" adPlacement:@""];
    }
}

INTERSTITIAL

WHEN REQUESTING INTERSTITIAL AD

  • When making a load request of an ad send the following ad event as well:
if (!self.interstitial.isReady) {
    // create ad request with required parameters
    // request new ad to be fetched
    [self.interstitial loadRequest:request];

    // send ad event when loading
    [GameAnalytics addAdEventWithAction:GAAdActionRequest adType:GAAdTypeInterstitial adSdkName:@"admob" adPlacement:@""];
}

WHEN INTERSTITIAL AD IS LOADED

  • If the ad loads successfully send the following ad event when interstitialDidReceiveAd:(GADInterstitial *)ad is called.
  • The following example is a method for how you could handle this.
- (void)interstitialDidReceiveAd:(GADInterstitial *)ad {
    // To enable tracking of errors related to ad network requests we recommend using a local variable to store this information.
    // Here we use a "latestRewardedVideoError" variable
    // In this method we clear the error variable as the request is successful
    latestInterstitialError = nil;
    // send ad event when loaded
    [GameAnalytics addAdEventWithAction:GAAdActionLoaded adType:GAAdTypeInterstitial adSdkName:@"admob" adPlacement:@""];
}

WHEN INTERSTITIAL AD FAILS TO LOAD

  • If the ad fails to load we track the latest when interstitial:(GADInterstitial *)ad didFailToReceiveAdWithError:(GADRequestError *)error is called.
  • The following example is a method for how you could handle this.
- (void)interstitial:(GADInterstitial *)ad
    didFailToReceiveAdWithError:(GADRequestError *)error {
    // keep track of latest error (optional, only needed if you want to track errors for "FailedShow" ad event)
    latestInterstitialError = error;
}

SHOWING AN INTERSTITIAL AD

  • When you want to show an ad, it is either available or not.
  • Call the ad event, when the ad is not available.
if (self.interstitial.isReady) {
    [self.interstitial presentFromRootViewController:self];
}
else
{
    // send ad event, for tracking a potential error
    // the getLatestAdError method is describer earlier
    [GameAnalytics addAdEventWithAction:GAAdActionFailedShow adType:GAAdTypeInterstitial adSdkName:@"admob" adPlacement:@"" noAdReason:getLatestAdError(latestRewardedVideoError)];

    // OR..  if you don't want to track errors
    [GameAnalytics addAdEventWithAction:GAAdActionFailedShow adType:GAAdTypeInterstitial adSdkName:@"admob" adPlacement:@""];
}

TRACK INTERSTIAL BEING SHOW

  • For interstitials we track an event at the time it is being shown
  • The following example is a method for how you could handle this.
- (void)interstitialWillPresentScreen:(GADInterstitial *)ad {
    // send ad event
    [GameAnalytics addAdEventWithAction:GAAdActionShow adType:GAAdTypeInterstitial adSdkName:@"admob" adPlacement:@""];
}

TRACK WHEN USER CLICKS ON AD (AND LEAVE APPLICATION)

  • Call event when interstitialWillLeaveApplication:(GADInterstitial *)ad is called
  • The following example is a method for how you could handle this.
- (void)interstitialWillLeaveApplication:(GADInterstitial *)ad {
    // send ad event
    [GameAnalytics addAdEventWithAction:GAAdActionClicked adType:GAAdTypeInterstitial adSdkName:@"admob" adPlacement:@""];
}

BANNER AD

WHEN REQUESTING BANNER AD

  • When making a load request of an ad send the following ad event as well:
// create ad request with required parameters
// request new ad to be fetched
[self.bannerView loadRequest:[GADRequest request]];
[GameAnalytics addAdEventWithAction:GAAdActionRequest adType:GAAdTypeBanner adSdkName:@"admob" adPlacement:@""];

WHEN BANNER AD IS LOADED

  • If the ad loads successfully send the following ad event when adViewDidReceiveAd:(GADBannerView *)adView is called.
  • The following example is a method for how you could handle this.
- (void)adViewDidReceiveAd:(GADBannerView *)adView {
    // send ad events - load and show
    [GameAnalytics addAdEventWithAction:GAAdActionLoaded adType:GAAdTypeBanner adSdkName:@"admob" adPlacement:@""];
    [GameAnalytics addAdEventWithAction:GAAdActionShow adType:GAAdTypeBanner adSdkName:@"admob" adPlacement:@""];
}

WHEN AD FAILS TO LOAD

  • If the ad fails to load we track the latest when adView:(GADBannerView *)adViewdidFailToReceiveAdWithError:(GADRequestError *)error is called.
  • The following example is a method for how you could handle this.
- (void)adView:(GADBannerView *)adView
    didFailToReceiveAdWithError:(GADRequestError *)error {
    // send ad events
    [GameAnalytics addAdEventWithAction:GAAdActionFailedShow adType:GAAdTypeBanner adSdkName:@"admob" adPlacement:@"" noAdReason:getLatestAdError(error)];

    // OR .. if you don't want to track errors
    [GameAnalytics addAdEventWithAction:GAAdActionFailedShow adType:GAAdTypeBanner adSdkName:@"admob" adPlacement:@""];
}

TRACK WHEN USER CLICKS ON BANNER AD

  • For admob the banner ad click is registered on the adViewWillLeaveApplication:(GADBannerView *)adView callback.
  • Call an ad event at this time for banner ads.
  • The following example is a method for how you could handle this.
- (void)adViewWillLeaveApplication:(GADBannerView *)adView {
    // send ad event
    [GameAnalytics addAdEventWithAction:GAAdActionClicked adType:GAAdTypeBanner adSdkName:@"admob" adPlacement:@""];
}
Field Type Description Example
adAction enum A defined enum for ad action (for example clicked). GAAdActionClicked
adType enum A defined enum for ad type (for example interstitial). GAAdTypeInterstitial
adSdkName string Name of the Ad/Ad mediation SDK. admob
adPlacement string Identifier of ad in the game or the placement of it. level_complete_ad
adPlacement string Identifier of ad in the game or the placement of it. level_complete_ad
duration int Optional. Only used for video ads to track how long the user watched the video for. 10
noAdReason enum Optional. Used to track the reason for not being able to show an ad when needed (for example no fill). GAAdErrorNoFill

ℹ️
For more information about the ad event go here.

#

Resource

Copy link to clipboard
Resource events are used to register the flow of your in-game economy (virtual currencies) – the sink (subtract) and the source (add) for each virtual currency.

Before calling the resource event it is needed to specify what discrete values can be used for currencies and item types in the Configuration phase.

source (add)

Add gem currency from an in-app purchase.

//Objective-C [GameAnalytics addResourceEventWithFlowType:GAResourceFlowTypeSource currency:@"Gems" amount:@400 itemType:@"IAP" itemId:@"Coins400"];

// Swift
GameAnalytics.addResourceEvent(with: GAResourceFlowTypeSource currency:"Gems" amount:400 itemType:"IAP" itemId:"Coins400")

sink (subtract)

Subtract gem currency to buy an item.

// Objective-C
[GameAnalytics addResourceEventWithFlowType:GAResourceFlowTypeSink currency:@"Gems" amount:@400 itemType:@"Weapons" itemId:@"SwordOfFire"];

// Swift
GameAnalytics.addResourceEvent(with: GAResourceFlowTypeSink currency:"Gems" amount:400 itemType:"Weapons" itemId:"SwordOfFire")

Subtract gem currency to source (buy) some amount of another virtual currency (BeamBooster).

// Objective-C 
[GameAnalytics addResourceEventWithFlowType:GAResourceFlowTypeSink currency:@"Gems" amount:@100 itemType:@"Boosters" itemId:@"BeamBooster5Pack"];
[GameAnalytics addResourceEventWithFlowType:GAResourceFlowTypeSource currency:@"BeamBooster" amount:@5 itemType:@"Gems" itemId:@"BeamBooster5Pack"];

// Swift
GameAnalytics.addResourceEvent(with: GAResourceFlowTypeSink currency:"Gems" amount:100 itemType:"Boosters" itemId:"BeamBooster5Pack")
GameAnalytics.addResourceEvent(with: GAResourceFlowTypeSource currency:"BeamBooster" amount:5 itemType:"Gems" itemId:"BeamBooster5Pack")

Subtract 3 BeamBooster currency that were used during a level.

// Objective-C
[GameAnalytics addResourceEventWithFlowType:GAResourceFlowTypeSink currency:@"BeamBooster" amount:@3 itemType:@"Gameplay" itemId:@"BeamBooster5Pack"];

// Swift
GameAnalytics.addResourceEvent(with: GAResourceFlowTypeSink currency:"BeamBooster" amount:3 itemType:"Gameplay" itemId:"BeamBooster5Pack")
Field Type Description Example
flowType enum A defined enum for sourcing and sinking resources. GAResourceFlowTypeSink
currency string The resource type/currency to track. Has to be one of the configured available resource currencies.This string can only contain [A-Za-z] characters. Gems, BeamBoosters, Coins
amount float Amount sourced or sinked. 0 or negative numbers are not allowed. 100.0
itemType string For sink events it can describe an item category you are buying (Weapons) or a place (Gameplay) the currency was consumed. For source events it can describe how the currency was gained. For example “IAP” (for in-app purchase) or from using another currency (Gems). Has to be one of the configured available itemTypes. Weapons, IAP, Gameplay, Boosters
itemId string For sink events it can describe the specific item (SwordOfFire) gained. If consumed during Gameplay you can simply use “Consumed”. For source events it describes how the player got the added currency. This could be buying a pack (BoosterPack5) or earned through Gameplay when completing a level (LevelEnd). BoosterPack5, SwordOfFire, LevelEnd, Coins400

Be careful to not call the resource event too often! In a game where the user collect coins fairly fast you should not call a Source event on each pickup. Instead you should count the coins and send a single Source event when the user either complete or fail the level.

Read more information regarding the resource event here.

Make sure you locally test the instrumented resource events and they are being sent without any difficulties by verifying the info and verbose logs at runtime.

#

Progression

Copy link to clipboard

Progression events are used to track attempts at completing some part of a game (level, area).

A defined area follow a 3 tier hierarchy structure (could be world:stage:level) to indicate what part of the game the player is trying to complete.

When a player is starting a progression attempt a start event should be added. When the player then finishes the attempt a fail or complete event should be added along with a score if needed.

Add a progression start event.

// Objective-C
[GameAnalytics addProgressionEventWithProgressionStatus:GAProgressionStatusStart progression01:@"world01" progression02:@"stage01" progression03:@"level01"];

// Swift
 GameAnalytics.addProgressionEvent(with: GAProgressionStatusStart progression01:"world01" progression02:"stage01" progression03:"level01")

Add a progression start event using only Progression1.

// Objective-C
[GameAnalytics addProgressionEventWithProgressionStatus:GAProgressionStatusStart progression01:@"world01" progression02:nil progression03:nil];

// Swift
GameAnalytics.addProgressionEvent(with: GAProgressionStatusStart progression01:"world01" progression02:nil progression03:nil)

Add a progression complete event with or without score.

// Objective-C
[GameAnalytics addProgressionEventWithProgressionStatus:GAProgressionStatusComplete progression01:@"world01" progression02:@"stage01" progression03:@"level01" score:15000];

// Swift
GameAnalytics.addProgressionEvent(with: GAProgressionStatusComplete progression01:"world01" progression02:"stage01" progression03:"level01" score:15000)

It is not required to use all 3 if your game does not have them.

  • progression01
  • progression01 and progression02
  • progression01 and progression02 and progression03
Field Type Description Example
progressionStatus enum Status of added progression GAProgressionStatusStart GAProgressionStatusFail GAProgressionStatusComplete
progression01 string Required progression location. World01
progression02 string Not required. Use if needed. Stage01
progression03 string Not required. Use if needed. Level01
score integer An optional score when a user completes or fails a progression attempt. 1023

Read more information regarding the progression event here.

#

Error

Copy link to clipboard

Used to track custom error events in the game. You can group the events by severity level and attach a message. By default the SDK will automatically send error events for uncaught exceptions for more information on this and how to disable it click here.

To add a custom error event call the following function:

// Objective-C
[GameAnalytics addErrorEventWithSeverity:GAErrorSeverityDebug message:@"Something went bad in some of the smelly code!"];

// Swift
GameAnalytics.addErrorEvent(with: GAErrorSeverityDebug message:"Something went bad in some of the smelly code!")
Field Type Description Example
severity enum Severity of error GAErrorSeverityDebug GAErrorSeverityInfo< GAErrorSeverityWarning GAErrorSeverityError GAErrorSeverityCritical
message string Error message (can be nil) “Error when entering level12”

Read more information regarding the error even here.

#

Design Event

Copy link to clipboard

Every game is special. Therefore some needed events might not be covered by our other event types. The design event is available for you to add your own eventid hierarchy.

Please note that custom dimensions and progression filters will not be added on design and error events. Therefore you cannot (at the moment) filter by these when viewing design or error metrics.

To add a design event call the following method.

// Objective-C
[GameAnalytics addDesignEventWithEventId:@"Kill:Sword:Robot"];

// Swift
GameAnalytics.addDesignEvent(withEventId: "Kill:Sword:Robot")

It is also possible to add a float value to the event.

// Objective-C
[GameAnalytics addDesignEventWithEventId:@"BossFights:FireLord:KillTimeUsed" value:@234];

// Swift
GameAnalytics.addDesignEvent(withEventId: "BossFights:FireLord:KillTimeUsed" value:234)
Field Type Description Example
eventId string The eventId is a hierarchy string that can consist of 1-5 segments separated by ‘:’. Each segment can have a max length of 32. The segments can be written only with a-zA-Z0-9 characters. “StartGame:ClassLevel1_5”, “StartGame:ClassLevel6_10”
value float A float event tied to the eventId. Will result in sum & mean values being available. 34.5

It is important to not generate an excessive amount of unique nodes possible in the event hierarchy tree.

A bad implementation example: [level_name]:[weapon_used]:[damage_done]. level_name could be 100 values, weapon_used could be 300 values and damage_done could be 1-5000 perhaps. This will generate an event hierarchy with: 100 * 300 * 5000 = 1.5M possible nodes. This is far too many. Also the damage should be put as a value and not in the event string.

The processing will perhaps be blocked for a game doing this and cause other problems when browsing our tool.

The maximum amount of unique nodes generated should be around 10k.

For more information on the design event go here.

#

remote configs

Copy link to clipboard

#

Configs

Copy link to clipboard

Product Update – Remote Configs

We are excited to announce the upcoming release of Remote Configs – our new tool that will replace Configs.

Remote Configs will be released in October 2019 with a new user interface and updated SDKs.

You can continue to use the current version of Configs. We will soon be sharing more information and our migration guide.

If you have any questions, please don’t hesitate to reach out.

Register to events for whenever the Remote Configs is updated with new values:

- (void) onRemoteConfigsUpdated
{
    // add your code here
}

[GameAnalytics setRemoteConfigsDelegate:self];

To manual check if Remote Configs is ready (has been populated with values) you can call this:

if([GameAnalytics isRemoteConfigsReady])
{
    // the remote configs is ready, add your code here
}

To get values out of a populated Remote Configs use the following methods:

// Without custom default value (using normal default value)
NSString *value = [GameAnalytics getRemoteConfigsValueAsString:@"key"];
// With custom default value
NSString *valueWithCustomDefaultValue = [GameAnalytics getRemoteConfigsValueAsString:@"key" defaultValue:@"myDefaultValue"];

If the specified key is not found in the Remote Configs it will return the default value either “normal” or “custom” default value.

#

additional calls

Copy link to clipboard

#

Custom Dimensions

Copy link to clipboard

GameAnalytics support the use of 3 custom dimensions.

  • Custom01
  • Custom02
  • Custom03

During the game it is possible to set the active value for each custom dimension dynamically. Once a dimension is set it will be persisted across sessions/game-start and automatically be added to all event categories. Remember you have to set the custom dimensions before initialzing the SDK (but after setting the available custom dimensions) to be able to add the dimensions to the first session start event.

Setting each custom dimension.

// Objective-C
[GameAnalytics setCustomDimension01:@"ninja"];
[GameAnalytics setCustomDimension02:@"dolphin"];
[GameAnalytics setCustomDimension03:@"horde"];


// Swift
GameAnalytics.setCustomDimension01("ninja")
GameAnalytics.setCustomDimension02("dolphin")
GameAnalytics.setCustomDimension03("horde")
Field Type Description Example
customDimension string One of the available dimension values set in the configuration phase. Will persist cross session. Set to nil to reset. ninja

To reset a custom dimension during a session simply set the dimension name to nil. For instance, to reset the custom dimension 01 the following call needs to be written:

// Objective-C
[GameAnalytics setCustomDimension01:nil];

// Swift
GameAnalytics.setCustomDimension01(nil)

Read more about custom dimensions here.

#

User Information

Copy link to clipboard
During the game it is possible to set information about your users that will then be annotated to all other events.

  • gender
  • Facebook ID
  • birthyear (age)

ℹ️
These user values will persist cross session/game-launch. Set them to nil to reset.

Set gender.

// Objective-C
[GameAnalytics setGender:@"female"];

// Swift
GameAnalytics.setGender("female")

Set birthyear.

// Objective-C
[GameAnalytics setBirthYear:1980];

// Swift
GameAnalytics.setBirthYear(1980)

Set Facebook ID.

// Objective-C
[GameAnalytics setFacebookId:@"123456789012345"];

// Swift
GameAnalytics.setFacebookId("123456789012345")
Field Type Description Example
gender string Gender of player. female, male
birthYear integer The year the player was born. 1980
facebookId string Facebook Id of the player. 123456789012345

#

Error Reporting

Copy link to clipboard

By default the SDK will automatically send error events for uncaught exceptions. This can be disabled by calling this before the SDK has been initialized:

// Objective-C
[GameAnalytics setEnabledErrorReporting:NO];

// Swift
GameAnalytics.setEnabledErrorReporting(false)

If you are using other crash/error reporting services like for example Crashlytics together GameAnalytics’ automatic error reporting it can in some cases give problems but the SDK makes sure to call any error handlers set prior to GameAnalytics error handler.

#

debug & verify

Copy link to clipboard
#

Debug

Copy link to clipboard

The SDK is designed to be as silent as possible and use very few resources. You will therefore not get much information by default in the Xcode console. We have 2 different debug log types that can be enabled / disabled independently.

  • info log
  • verbose log

Info log

Short messages will be output when enabled explaining when some action is being performed by the SDK. Sometimes cropping text / values to make it more readable. Enable info log when implementing the SDK – remember to turn it off in production!

// Objective-C
[GameAnalytics setEnabledInfoLog:YES];

// Swift
GameAnalytics.setEnabledInfoLog(true)
 Info/GameAnalytics: Add DESIGN event: {eventId:someEvent, value:0}
 Info/GameAnalytics: Add DESIGN event: {eventId:someOtherEvent, value:100}
 Info/GameAnalytics: Add ERROR event: {severity:info, message:This is some in}

Verbose Log

Console output when each event is added (all fields) in JSON string format. This is the data being submitted to the GA servers for each event. Enable verbose log when troubleshooting events.

// Objective-C
[GameAnalytics setEnabledVerboseLog:YES];

// Swift
GameAnalytics.setEnabledVerboseLog(true)

This can result in a lot of text. When troubleshooting/debugging events it is therefore recommended to enable/disable when performing the action that need inspection. Troubleshooting example.

// Objective-C
// enable verbose log
[GameAnalytics setEnabledVerboseLog:YES];
// add event you need to troubleshoot / inspect
[GameAnalytics addDesignEventWithEventId:@"Some:Event" value:@100];
// disable verbose log 
[GameAnalytics setEnabledVerboseLog:NO];

// Swift
// enable verbose log
GameAnalytics.setEnabledVerboseLog(true)
// add event you need to troubleshoot / inspect
GameAnalytics.addDesignEvent(withEventId: "Some:Event" value:100)
// disable verbose log 
GameAnalytics.setEnabledVerboseLog(false)
#

Verify

Copy link to clipboard

Enable the Info Log to verify that events are being sent from your game project without any issues being reported. Events submitted should register after a minor delay in our realtime dashboard in the GameAnalytics dashboard.

Read more about the realtime dashboard and our data processing.

#

how does it work?

Copy link to clipboard
#

Session Handling

Copy link to clipboard

By default the SDK will handle session start/end automatically, but it is also possible to manually control this yourself.

Be aware that the initialization will always automatically start the first session even with manual session handling.

Automatic session handling

The automatic session handling will track the focused time the user is spending in your game – from game launch to the user leaving the game.

session start

On iOS a new session will start once the SDK is initialized or when the game is going to foreground.

session end

A session will end once the game is going to background.

Manual session handling

You can enable/disable manual session handling if you wish to control when sessions end/start.

// Objective-C
[GameAnalytics setEnabledManualSessionHandling:YES];

// Swift
GameAnalytics.setEnabledManualSessionHandling(true)

You will then need to call endSession and startSession at the appropriate times.

With manual session handling it is recommended to also call endSession when the game event going to background is fired. This will ensure a correct session close when users click the home or on/off button.

endSession()

// Objective-C
[GameAnalytics endSession];

// Swift
GameAnalytics.endSession()

This will end a session if:

  • manual session handling is enabled
  • a session is active
  • SDK is initialized (initialize will start a session automatically)

startSession()

// Objective-C
[GameAnalytics startSession];

// Swift
GameAnalytics.startSession()

This will start a new session if:

  • manual session handling is enabled
  • SDK is initialized (initialize will start a session automatically)

If a current session is active then it will end the current session and start a new one.

Behind the scenes

This is what happens when the session is starting or ending.

Session start

  1. Generate new session.
  2. Add a session start event (a “user” event).
  3. Start the periodic activation of submitting queued events.
  4. Next event submit will fix potential missing session_end from earlier sessions.

Session end

  1. Stop the periodic activation of submitting queued events.
  2. Add a session_end event.
  3. Submit queued events.

If you are deploying testflight builds then please note that user id’s are handled a bit differently.

The testflight environment is lacking any useful identifiers (they are random each app launch). The SDK will create a random user_id internally and use it for each app launch. If the game is removed and installed again, then a new user_id will be created though and it will register as a new user.

Once it is deployed to the app store (or run locally on an actual device) it will track users (returning etc.) correctly.

#

Event Queue

Copy link to clipboard

Whenever an event is added (and validated) it will be added to a local database queue.

Interval

Every 8 seconds the SDK will start a task for submitting queued events since last submit. This processing is done in a separate low-priority thread that will have minimum impact on performance. The payload is gzipped and will therefore only consume a small amount of bandwidth.

Offline

When a device is offline the events are still added to the queue. When the device is online it will submit.

#

Thread Handling

Copy link to clipboard
For the iOS platform almost every piece of the SDK code is run using a dedicated low-priority serial thread queue to avoid UI lag or sudden performance spikes.

 dispatch_queue_create("gameAnalyticsQueue", DISPATCH_QUEUE_SERIAL);

The queue will execute each task sequentially. If the SDK add several tasks to the queue then each will have to wait until it’s turn. A task could be adding an event or submitting queued events. Consider this example with 3 calls.

// Objective-C
// Configure build version
[GameAnalytics configureBuild:@"alpha 0.1.0"];
// Initialize
[GameAnalytics initializeWithGameKey:@"12341234123412341234123412341234" gameSecret:@"1234123412341234123412341234123412341234"];
// Add Design event
[GameAnalytics addDesignEventWithEventId:@"Some:Event"];

// Swift
// Configure build version
GameAnalytics.configureBuild("alpha 0.1.0");
// Initialize
GameAnalytics.initialize(withGameKey: "12341234123412341234123412341234" gameSecret:@"1234123412341234123412341234123412341234")
// Add Design event
GameAnalytics.addDesignEvent(withEventId: "Some:Event")

The configureBuild is required to be called before initialize is completely finished. The design event call is required after initialize is finished. The queuing will make sure that each task is completely finished before proceeding to the next one.

Contact support

We’ll get back to you ASAP.

Report a bug

Let us know more details.