Getting Started Guide for Objective-C

This developer guide describes how to implement Google Tag Manager in a mobile application.

Introduction

Google Tag Manager enables developers to change configuration values in their mobile applications using the Google Tag Manager interface without having to rebuild and resubmit application binaries to app marketplaces.

This is useful for managing any configuration values or flags in your application that you may need to change in the future, including:

  • Various UI settings and display strings
  • Sizes, locations, or types of ads served in your application
  • Game settings

Configuration values may also be evaluated at runtime using rules, enabling dynamic configurations such as:

  • Using screen size to determine ad banner size
  • Using language and location to configure UI elements

Google TagManager also enables the dynamic implementation of tracking tags and pixels in applications. Developers can push important events into a data layer and decide later which tracking tags or pixels should be fired. TagManager currently supports the following tags:

  • Google Mobile App Analytics
  • Custom Function Call tag

Before you Begin

Before using this getting started guide, you'll need the following:

If you're new to Google Tag Manager, we recommend that you learn more about containers, macros, and rules (Help Center) before continuing this guide.

Getting Started

This section will guide developers through a typical Tag Manager workflow:

  1. Add the Google Tag Manager SDK to your project
  2. Set Default Container Values
  3. Open the Container
  4. Get Configuration Values from the Container
  5. Push Events to the DataLayer
  6. Preview & Publish the Container

1. Adding the Google Tag Manager SDK to Your Project

Before using the Google Tag Manager SDK, you'll need to add libGoogleAnalyticsServices.a and the Google Tag Manager (GTM) header files from the Library directory of the SDK package to your project.

Next, add the following to your application target's linked libraries if they are not already present:

  • CoreData.framework
  • SystemConfiguration.framework
  • libz.dylib
  • libsqlite3.dylib
  • libGoogleAnalyticsServices.a

If you would like your application to access the identifier for advertisers (IDFA) and tracking flag provided by that framework through the Google Tag Manager SDK macros, you'll also need to link these additional libraries:

  • libAdIdAccess.a
  • AdSupport.framework

2. Adding a Default Container File to your Project

Google Tag Manager uses a default container on the first run of your application. The default container will be used until the app is able to retrieve a fresh container over the network.

To download and add a default container binary to your application, follow these steps:

  1. Sign in to the Google Tag Manager web interface.
  2. Select the Version of the container you'd like to download.
  3. Click the Download button to retrieve the container binary.
  4. Add the binary file to your project's root directory and to the "Supporting Files" folder in your project.

The default filename should be the container ID (for example GTM-1234). Once you've downloaded the binary file, be sure to remove the version suffix from the filename to ensure you follow the correct naming convention.

Although using the binary file is recommended, if your container does not contain rules or tags, you may choose to use a simple property list or JSON file instead. The file should be located in the main bundle and should follow this naming convention: <Container_ID>.<plist|json>. For example, if your container ID is GTM-1234, you could specify your default container values in a property list file named GTM-1234.plist.

3. Opening a Container

Before retrieving values from a container, your application needs to open the container. Opening a container will load it from disk (if available), or will request it from the network (if needed).

The easiest way to open a container on iOS is by using openContainerWithId:tagManager:openType:timeout:notifier:, as in the following example:

// MyAppDelegate.h
// This example assumes this file is using ARC.
#import <UIKit/UIKit.h>

@class TAGManager;
@class TAGContainer;

@interface MyAppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) TAGManager *tagManager;
@property (nonatomic, strong) TAGContainer *container;

@end


// MyAppDelegate.m
// This example assumes this file is using ARC.
#import "MyAppDelegate.h"
#import "TAGContainer.h"
#import "TAGContainerOpener.h"
#import "TAGManager.h"

@interface MyAppDelegate ()<TAGContainerOpenerNotifier>
@end

@implementation MyAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.tagManager = [TAGManager instance];

  // Optional: Change the LogLevel to Verbose to enable logging at VERBOSE and higher levels.
  [self.tagManager.logger setLogLevel:kTAGLoggerLogLevelVerbose];

  /*
   * Opens a container.
   *
   * @param containerId The ID of the container to load.
   * @param tagManager The TAGManager instance for getting the container.
   * @param openType The choice of how to open the container.
   * @param timeout The timeout period (default is 2.0 seconds).
   * @param notifier The notifier to inform on container load events.
   */
  [TAGContainerOpener openContainerWithId:@"GTM-XXXX"   // Update with your Container ID.
                               tagManager:self.tagManager
                                 openType:kTAGOpenTypePreferFresh
                                  timeout:nil
                                 notifier:self];

  // Method calls that don't need the container.

  return YES;
}

// TAGContainerOpenerNotifier callback.
- (void)containerAvailable:(TAGContainer *)container {
  // Note that containerAvailable may be called on any thread, so you may need to dispatch back to
  // your main thread.
  dispatch_async(dispatch_get_main_queue(), ^{
    self.container = container;
  });
}

// The rest of your app delegate implementation.

4. Getting Configuration Values from the Container

Once the container is open, configuration values may be retrieved using the <type>ForKey: methods:

// Retrieving a configuration value from a Tag Manager Container.

MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
TAGContainer *container = appDelegate.container;

// Get the configuration value by key.
NSString *title = [container stringForKey:@"title_string"];

Requests made with a non-existent key will return a default value appropriate to the type requested:

// Empty keys will return a default value depending on the type requested.

// Key does not exist. An empty string is returned.
NSString subtitle = [container stringForKey:@"Non-existent-key"];
[subtitle isEqualToString:@""]; // Evaluates to true.

5. Pushing Values to the DataLayer

The DataLayer is a map that enables runtime information about your app, such as touch events or screen views, to become available to Tag Manager macros and tags in a container.

For example, by pushing information about screen views into the DataLayer map, you can set up tags in the Tag Manager web interface to fire conversion pixels and tracking calls in response to those screenviews without needing to hard code them into your app.

Events are pushed to the DataLayer using push:

//
//  ViewController.m
//  Pushing an openScreen event with a screen name into the data layer.
//

#import "MyAppDelegate.h"
#import "TAGDataLayer.h"
#import "ViewController.h"

@implementation ViewController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // The container should have already been opened, otherwise events pushed to
    // the data layer will not fire tags in that container.
    TAGDataLayer *dataLayer = [TAGManager instance].dataLayer;

    [dataLayer push:@{@"event": @"openScreen", @"screenName": @"Home Screen"}];
}

// Rest of the ViewController implementation

@end

In the web interface, you can now create tags (like Google Analytics tags) to fire for each screen view by creating this rule: {{ event }} equals "openScreen". To pass the screen name to one of these tags, create a data layer macro that references the "screenName" key in the data layer. You can also create a tag (like a Google Ads conversion pixel) to fire only for specific screen views, by creating a rule where {{ event }} equals "openScreen" && {{ screenName }} equals "ConfirmationScreen".

6. Previewing & Publishing a Container

Macro values will always correspond to the current published version. Before publishing the latest version of a container, you can preview your draft container.

To preview a container, generate a preview URL in the Google Tag Manager web interface by selecting the version of the container you'd like to preview, and then selecting Preview. Hang on to this preview URL as you'll need it in later steps.

Preview URLs are available in the preview window of the Tag
           Manager web interface
Figure 1: Getting a preview URL from the Tag Manager web interface.

To enable container previews, you must add code to your app delegate implementation file and define the Google Tag Manager preview URL scheme in your project's property list.

First, add the following bolded code snippets to your app delegate file:

@implementation MyAppDelegate

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

  self.tagManager = [TAGManager instance];
  
  // Add the code in bold below to preview a Google Tag Manager container.
  // IMPORTANT: This code must be called before the container is opened.
  NSURL *url = [launchOptions valueForKey:UIApplicationLaunchOptionsURLKey];
  if (url != nil) {
    [self.tagManager previewWithUrl:url];
  }
  
  id<TAGContainerFuture> future =
      [TAGContainerOpener openContainerWithId:@"GTM-XXXX"    // Placeholder Container ID.
                                   tagManager:self.tagManager
                                     openType:kTAGOpenTypePreferNonDefault
                                      timeout:nil];

  // The rest of your method implementation.

  self.container = [future get];

  return YES;
}


// Add the code in bold below preview a Google Tag Manager container.
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {

  if ([self.tagManager previewWithUrl:url]) {
    return YES;
  }

  // Code to handle other urls.
  return NO;
}

Next, register the following URL identifier and URL scheme under the URL types key of your application's property list file:

URL identifier: your.package_name
URL scheme: tagmanager.c.your.package.name
Register the tag manager preview URL scheme in your application's
                property list file.
Figure 3: Adding the Tag Manager preview URL scheme to your application's property list file.

Open the link on an emulator or physical device to preview the draft container in your app.

When you're ready to make your draft configuration values available to your application, publish the container.

Advanced Configuration

Google Tag Manager for Mobile has a number of advanced configuration options that allow you to select values based on runtime conditions using rules, manually refresh the container, and get additional options for opening containers. The following sections outline several of the most common advanced configurations.

Advanced Options for Opening Containers

The Google Tag Manager SDK provides several methods for opening containers that can give you more control over the loading process:

openContainerById:callback:

openContainerById:callback: is the lowest level and most flexible API for opening a container. It returns immediately with a default container and also asynchronously loads a container from disk or the network if no saved container exists, or if the saved container is not fresh (> 12 hours old).

@interface ContainerCallback : NSObject<TAGContainerCallback>

@end

@implementation ContainerCallback

/**
 * Called before the refresh is about to begin.
 *
 * @param container The container being refreshed.
 * @param refreshType The type of refresh which is starting.
 */
- (void)containerRefreshBegin:(TAGContainer *)container
                  refreshType:(TAGContainerCallbackRefreshType)refreshType {
  // Notify UI that container refresh is beginning.
}

/**
 * Called when a refresh has successfully completed for the given refresh type.
 *
 * @param container The container being refreshed.
 * @param refreshType The type of refresh which completed successfully.
 */
- (void)containerRefreshSuccess:(TAGContainer *)container
                    refreshType:(TAGContainerCallbackRefreshType)refreshType {
  // Notify UI that container is available.
}

/**
 * Called when a refresh has failed to complete for the given refresh type.
 *
 * @param container The container being refreshed.
 * @param failure The reason for the refresh failure.
 * @param refreshType The type of refresh which failed.
 */
- (void)containerRefreshFailure:(TAGContainer *)container
                        failure:(TAGContainerCallbackRefreshFailure)failure
                    refreshType:(TAGContainerCallbackRefreshType)refreshType {
  // Notify UI that container request has failed.
}
@end

Throughout the loading process, openContainerById:callback: issues several lifecycle callbacks so that your code can find out when the loading request begins, whether and why it fails or succeeds, and whether the container was ultimately loaded from disk or network.

Unless it is acceptable for your application to use the default values, you will need to use these callbacks to know when a saved or network container has loaded. Note that you will be unable to load a saved or network container if this is the first time the app is run and there's no network connection.

openContainerById:callback: passes the following enum values as arguments to these callbacks:

RefreshType

ValueDescription
kTAGContainerCallbackRefreshTypeSaved The refresh request is loading a locally saved container.
kTAGContainerCallbackRefreshTypeNetwork The refresh request is loading a container over the network.

RefreshFailure

ValueDescription
kTAGContainerCallbackRefreshFailureNoSavedContainer There is no saved container available.
kTAGContainerCallbackRefreshFailureIoError An I/O error prevented refreshing the container.
kTAGContainerCallbackRefreshFailureNoNetwork There is no network connection available.
kTAGContainerCallbackRefreshFailureNetworkError A network error has occurred.
kTAGContainerCallbackRefreshFailureServerError An error on the server has occurred.
kTAGContainerCallbackRefreshFailureUnknownError An error that can't be categorized has occurred.

Methods for Opening Non-Default and Fresh Containers

TAGContainerOpener wraps openContainerById:callback: and provides two convenience methods for opening containers: openContainerWithId:tagManager:openType:timeout:notifier: and openContainerWithId:tagManager:openType:timeout:.

Each of these methods takes an enumeration requesting either a non-default or fresh container.

kTAGOpenTypePreferNonDefault is recommended for most applications and attempts to return the first available non-default container within a given timeout period, either from disk or network, even if that container is greater than 12 hours old. If it returns a stale saved container, it will also make an asynchronous network request for a fresh one. When using kTAGOpenTypePreferNonDefault, a default container will be returned if no other container is available, or if the timeout period is exceeded.

kTAGOpenTypePreferFresh attempts to return a fresh container from either disk or network within the given timeout period. It returns a saved container if a network connection is unavailable and/or the timeout period is exceeded.

It is not recommended to use kTAGOpenTypePreferFresh in places where a longer request time may noticeably affect user experience, such as with UI flags or display strings. You may also use TAGContainer::refresh at any time to force a network container request.

Both of these convenience methods are non-blocking. openContainerWithId:tagManager:openType:timeout: returns a TAGContainerFuture object, whose get method returns a TAGContainer as soon as it has loaded (but that will block until then). The openContainerWithId:tagManager:openType:timeout:notifier: method takes a single callback, called when the container is available. Both methods have a default timeout period of 2.0 seconds.

Evaluating Macros at Runtime using Rules

Containers can evaluate values at runtime using rules. Rules may be based on criteria like device language, platform, or any other macro value. For example, rules can be used to select a localized display string based on the language of the device at runtime. This can be configured by using the following rule:

A rule is used to select display strings based on device language at
            runtime: language equals es. This rule uses the pre-defined language
            macro and a two character ISO 639-1 language code.
Figure 1:Adding a rule to enable a value collection macro only for devices configured to use the Spanish language.

You can then create value collection macros for each language, and add this rule to each macro, inserting the appropriate language code. When this container is published, your application will be able to display localized display strings, depending on the language of the user's device at runtime.

Note that if your default container needs rules, you must use a binary container file as your default container.

Learn more about configuring rules (Help Center).

Binary Default Container Files

Default containers that need rules should use a binary container file instead of a property list file or JSON file as the default container. Binary containers offer support for determining macro values at runtime with Google Tag Manager rules, whereas property list or JSON files do not.

Binary container files may be downloaded from the Google Tag Manager web interface and should be added to your main application bundle following this naming convention: GTM-XXXX, where the filename represents your container id.

In cases where a property list file and/or JSON file as well as a binary container file are present, the SDK will use the binary container file as the default container.

Using Function Call Macros

Function Call Macros are macros that are set to the return value of a specified function in your application. Function Call Macros can be used to incorporate runtime values with your Google Tag Manager rules, such as determine at runtime which price to display to a user based on the configured language and currency of a device.

To configure a function call macro:

  1. Define the function call macro in the Google Tag Manager web interface. Arguments may optionally be configured as key-value pairs.
  2. Define a handler that implements the TAGFunctionCallMacroHandler protocol:
    // MyFunctionCallMacroHandler.h
    #import "TAGContainer.h"
    
    // The function name field of the macro, as defined in the Google Tag Manager
    // web interface.
    extern NSString *const kMyMacroFunctionName;
    
    @interface MyFunctionCallMacroHandler : NSObject<TAGFunctionCallMacroHandler>
    
    @end
    
    
    // MyFunctionCallMacroHandler.m
    #import "MyFunctionCallMacroHandler.h"
    
    // Corresponds to the function name field in the Google Tag Manager interface.
    NSString *const kMyMacroFunctionName = @"myConfiguredFunctionName";
    
    @implementation MacroHandler
    
    - (id)valueForMacro:(NSString *)functionName parameters:(NSDictionary *)parameters {
    
      if ([functionName isEqualToString:kMyMacroFunctionName]) {
        // Process and return the calculated value of this macro accordingly.
        return macro_value;
      }
      return nil;
    }
    
    @end
    
  3. Register the handler using TAGContainer::registerFunctionCallMacroHandler:forMacro: and the function name specified in the Google Tag Manager interface:
  4. //
    // MyAppDelegate.h
    //
    #import <UIKit/UIKit.h>
    
    @interface MyAppDelegate : UIResponder <UIApplicationDelegate>
    
    @end
    
    
    //
    // MyAppDelegate.m
    //
    #import "MyAppDelegate.h"
    #import "MyFunctionCallMacroHandler.h"
    #import "TAGContainer.h"
    #import "TAGContainerOpener.h"
    #import "TAGManager.h"
    
    @implementation MyAppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
      // Open the container.
      id<TAGContainerFuture> future =
          [TAGContainerOpener openContainerWithId:@"GTM-XXXX"    // Placeholder Container ID.
                                       tagManager:[TAGManager instance]
                                         openType:kTAGOpenTypePreferNonDefault
                                          timeout:nil];
    
      // Method calls that don't need the container.
    
      self.container = [future get];
    
      // Register a function call macro handler using the macro name defined
      // in the Google Tag Manager web interface.
      [self.container registerFunctionCallMacroHandler:[[MyFunctionCallMacroHandler alloc] init]
                                              forMacro:kMyMacroFunctionName];
    }
    
    @end
    

Using Function Call Tags

Function Call Tags enable pre-registered functions to be executed whenever an event is pushed into the data layer and the tag rules evaluate to true.

To configure a function call tag:

  1. Define the function call tag in the Google Tag Manager web interface. Arguments may optionally be configured as key-value pairs.
  2. Implement the TAGFunctionCallTagHandler protocol:
    //
    // MyFunctionCallTagHandler.h
    //
    
    #import "TAGContainer.h"
    
    extern NSString *const kMyTagFunctionName;
    
    @interface MyFunctionCallTagHandler : NSObject<TAGFunctionCallTagHandler>
    
    @end
    
    
    //
    // MyFunctionCallTagHandler.m
    //
    
    // Corresponds to the function name field in the Google Tag Manager interface.
    NSString *const kMyTagFunctionName = @"myConfiguredFunctionName";
    
    @implementation MyFunctionCallTagHandler
    
    /**
     * This method will be called when any custom tag's rule(s) evaluate to true and
     * should check the functionName and process accordingly.
     *
     * @param functionName corresponds to the function name field, not tag
     *     name field, defined in the Google Tag Manager web interface.
     * @param parameters An optional map of parameters as defined in the Google
     *     Tag Manager web interface.
     */
    - (void)execute:(NSString *)functionName parameters:(NSDictionary *)parameters {
    
      if ([functionName isEqualToString:kMyTagFunctionName]) {
        // Process accordingly.
      }
    }
    @end
    
  3. Register the function call tag handler using the tag name configured in the Google Tag Manager web interface:
  4. //
    // MyAppDelegate.h
    //
    #import <UIKit/UIKit.h>
    
    @interface MyAppDelegate : UIResponder <UIApplicationDelegate>
    
    @end
    
    
    //
    // MyAppDelegate.m
    //
    #import "MyAppDelegate.h"
    #import "MyFunctionCallTagHandler.h"
    #import "TAGContainer.h"
    #import "TAGContainerOpener.h"
    #import "TAGManager.h"
    
    @implementation MyAppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
      // Open the container.
      id<TAGContainerFuture> future =
          [TAGContainerOpener openContainerWithId:@"GTM-XXXX"    // Placeholder Container ID.
                                       tagManager:[TAGManager instance]
                                         openType:kTAGOpenTypePreferNonDefault
                                          timeout:nil];
    
      // Method calls that don't need the container.
    
      self.container = [future get];
    
      // Register a function call tag handler using the function name of the tag as
      // defined in the Google Tag Manager web interface.
      [self.container registerFunctionCallTagHandler:[[MyFunctionCallTagHandler alloc] init]
                                              forTag:kMyTagFunctionName];
    }
    @end
    

Setting a Custom Refresh Period

The Google Tag Manager SDK will attempt to retrieve a fresh container if the current container age exceeds 12 hours. To set a custom container refresh period, use NSTimer, as in the following example:

- (void)refreshContainer:(NSTimer *)timer {
  [self.container refresh];
}

self.refreshTimer = [NSTimer scheduledTimerWithTimeInterval:<refresh_interval>
                                                     target:self
                                                   selector:@selector(refreshContainer:)
                                                   userInfo:nil
                                                    repeats:YES];

Debugging with Logger

The Google Tag Manager SDK prints errors and warnings to logs by default. Enabling more verbose logging can be helpful for debugging and is possible by implementing your own Logger, as in this example:

// MyAppDelegate.h
// This example assumes this file is using ARC.
// This Logger class will print out not just errors and warnings (as the default
// logger does), but also info, debug, and verbose messages.
@interface MyLogger: NSObject<TAGLogger>
@end

@implementation MyLogger
- (void)error:(NSString *)message {
  NSLog(@"Error: %@", message);
}

- (void)warning:(NSString *)message {
  NSLog(@"Warning: %@", message);
}

- (void)info:(NSString *)message {
  NSLog(@"Info: %@", message);
}

- (void)debug:(NSString *)message {
  NSLog(@"Debug: %@", message);
}

- (void)verbose:(NSString *)message {
  NSLog(@"Verbose: %@", message);
}
@end

// MyAppDelegate.m
// This example assumes this file is using ARC.
@implementation MyAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.tagManager = [TAGManager instance];
  
  self.tagManager.logger = [[MyLogger alloc] init];
  
  // Rest of Tag Manager and method implementation.
  return YES;
}
// Rest of app delegate implementation.
@end

Or, you can set the LogLevel of the existing Logger by using TagManager::logger::setLogLevel, as in this example:

// Change the LogLevel to INFO to enable logging at INFO and higher levels.
self.tagManager = [TAGManager instance];
[self.tagManager.logger setLogLevel:kTAGLoggerLogLevelInfo];