Objective-C 入门指南

本开发者指南将介绍如何在移动应用中实现 Google 跟踪代码管理器。

简介

借助 Google 跟踪代码管理器,开发者可以使用 Google 跟踪代码管理器界面更改移动应用中的配置值,而无需重新生成应用二进制文件并将其重新提交到应用市场。

这对于管理应用中将来可能需要更改的任何配置值或标志非常有用,包括:

  • 各种界面设置和显示字符串
  • 您应用中投放的广告的尺寸、位置或类型
  • 游戏设置

配置值也可以在运行时使用规则求值,以实现以下动态配置:

  • 根据屏幕尺寸确定广告横幅的尺寸
  • 使用语言和位置配置界面元素

Google 跟踪代码管理器还支持在应用中动态实现跟踪代码和像素。开发者可以将重要事件推送到数据层,稍后再决定应触发哪些跟踪代码或像素。 跟踪代码管理器目前支持以下代码:

  • Google移动应用分析
  • 自定义函数调用代码

准备工作

在使用此入门指南之前,您需要做好以下准备:

如果您刚开始接触 Google 跟踪代码管理器,我们建议您先 详细了解容器、宏和规则(帮助中心),然后再继续本指南。

使用入门

本部分将引导开发者完成典型的跟踪代码管理器工作流程:

  1. 将 Google 跟踪代码管理器 SDK 添加到您的项目中
  2. 设置默认容器值
  3. 打开容器
  4. 从容器获取配置值
  5. 将事件推送到 DataLayer
  6. 预览和发布容器

1. 将 Google 跟踪代码管理器 SDK 添加到您的项目中

在使用 Google 跟踪代码管理器 SDK 之前,您需要将 SDK 软件包的 Library 目录中的 libGoogleAnalyticsServices.a 和 Google 跟踪代码管理器 (GTM) 头文件添加到您的项目中。

接下来,请将以下代码添加到您的应用目标的链接库中(如果它们尚不存在):

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

如果您希望应用访问该框架通过 Google 跟踪代码管理器 SDK 宏提供的广告客户标识符 (IDFA) 和跟踪标记,则还需要关联以下这些额外的库:

  • libAdIdAccess.a
  • AdSupport.framework

2. 向项目中添加默认容器文件

Google 跟踪代码管理器会在首次运行应用时使用默认容器。在应用能够通过网络检索到新容器之前,系统将使用默认容器。

如需下载默认容器二进制文件并将其添加到您的应用,请按以下步骤操作:

  1. 登录 Google 跟踪代码管理器网页界面。
  2. 选择您要下载的容器的版本
  3. 点击下载按钮以检索容器二进制文件。
  4. 将二进制文件添加到项目的根目录以及项目的“Supporting Files”文件夹中。

默认文件名应为容器 ID(例如 GTM-1234)。下载二进制文件后,请务必从文件名中移除版本后缀,以确保遵循正确的命名惯例。

虽然我们建议使用二进制文件,但如果您的容器不包含规则或标记,则可以选择使用简单的属性列表或 JSON 文件。该文件应位于主软件包中,并应遵循以下命名惯例:<Container_ID>.<plist|json>。例如,如果您的容器 ID 是 GTM-1234,您可以在名为 GTM-1234.plist 的属性列表文件中指定默认容器值。

3. 打开容器

在从容器中检索值之前,您的应用需要打开该容器。打开容器将从磁盘加载容器(如果可用),或从网络请求容器(如果需要)。

如需在 iOS 上打开容器,最简单的方法是使用 openContainerWithId:tagManager:openType:timeout:notifier:,如以下示例所示:

// 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. 从容器获取配置值

打开容器后,可以使用 <type>ForKey: 方法检索配置值:

// 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"];

使用不存在的键发出的请求将返回与所请求的类型相称的默认值:

// 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. 将值推送到 DataLayer

DataLayer 是一种映射,可让容器中的跟踪代码管理器宏和代码获取应用的运行时信息(例如触摸事件或屏幕浏览)。

例如,通过将屏幕浏览的相关信息推送到 DataLayer 地图,您可以在跟踪代码管理器网页界面中设置代码,从而触发转化像素和跟踪调用以响应这些屏幕浏览,而无需将其硬编码到您的应用中。

使用 push: 将事件推送到 DataLayer

//
//  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

在网页界面中,您现在可以创建以下规则,针对每次屏幕浏览创建要触发的代码(如 Google Analytics(分析)代码): 等于“openScreen”。要将屏幕名称传递到其中一个标记,请创建一个引用数据层中的“screenName”键的数据层宏。您还可以创建仅针对特定屏幕浏览触发的代码(例如 Google Ads 转化像素),方法是创建一条规则,其中 等于“openScreen” && 等于“ConfirmationScreen”。

6. 预览和发布容器

宏值将始终与当前已发布的版本相对应。 在发布容器的最新版本之前,您可以预览草稿容器。

若要预览容器,请在 Google 跟踪代码管理器网页界面中选择您要预览的容器版本,然后选择 Preview,从而生成预览网址。保留此预览网址,因为后续步骤中会用到。

您可以在跟踪代码管理器网页界面的预览窗口中找到预览网址
图 1 :从跟踪代码管理器网页界面获取预览网址。

如需启用容器预览,您必须向应用委托实现文件添加代码,并在项目的属性列表中定义 Google 跟踪代码管理器预览网址架构。

首先,将以下以粗体显示的代码段添加到应用委托文件中:

@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;
}

接下来,在应用的属性列表文件的网址类型键下注册以下网址标识符和网址架构:

URL identifier: your.package_name
URL scheme: tagmanager.c.your.package.name
在您应用的属性列表文件中注册跟踪代码管理器预览网址架构。
图 3 :将跟踪代码管理器预览网址架构添加到您应用的属性列表文件。

在模拟器或实体设备上打开链接,以预览应用中的草稿容器。

当您准备好将草稿配置值提供给您的应用时,请 发布容器

高级配置

Google 跟踪代码管理器移动版提供了许多高级配置选项,可让您使用规则根据运行时条件选择值、手动刷新容器以及获取打开容器的其他选项。以下部分概述了几种最常见的高级配置。

用于打开容器的高级选项

Google 跟踪代码管理器 SDK 提供了几种打开容器的方法,让您可以更好地控制加载过程:

openContainerById:callback:

openContainerById:callback: 是用于打开容器的最低级别且最灵活的 API。它会立即使用默认容器返回,并且如果不存在已保存的容器或保存的容器不是最新(存在时间超过 12 小时),还会从磁盘或网络异步加载容器。

@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

在整个加载过程中,openContainerById:callback: 会发出几个生命周期回调,以便您的代码能够了解加载请求何时开始、加载请求是否失败及成功,以及容器最终是从磁盘还是网络加载的。

除非应用可以使用默认值,否则您需要使用这些回调来了解何时加载了已保存的容器或网络容器。请注意,如果这是您首次运行该应用且没有网络连接,您将无法加载已保存的容器或网络容器。

openContainerById:callback: 会将以下 enum 值作为参数传递给这些回调:

RefreshType

说明
kTAGContainerCallbackRefreshTypeSaved 刷新请求正在加载本地保存的容器。
kTAGContainerCallbackRefreshTypeNetwork 刷新请求正在通过网络加载容器。

RefreshFailure

说明
kTAGContainerCallbackRefreshFailureNoSavedContainer 没有可用的已保存容器。
kTAGContainerCallbackRefreshFailureIoError I/O 错误导致无法刷新容器。
kTAGContainerCallbackRefreshFailureNoNetwork 无可用网络连接。
kTAGContainerCallbackRefreshFailureNetworkError 出现网络错误。
kTAGContainerCallbackRefreshFailureServerError 服务器上发生了错误。
kTAGContainerCallbackRefreshFailureUnknownError 发生了无法分类的错误。

打开非默认容器和新容器的方法

TAGContainerOpener 封装了 openContainerById:callback:,并提供两种便捷方法用于打开容器:openContainerWithId:tagManager:openType:timeout:notifier:openContainerWithId:tagManager:openType:timeout:

这两种方法都接受一个请求非默认容器或新容器的枚举。

对于大多数应用,我们建议使用 kTAGOpenTypePreferNonDefault,并且尝试在给定的超时期限内从磁盘或网络返回第一个可用的非默认容器,即使该容器存在时间超过 12 小时也是如此。如果它返回一个已保存容器的过时容器,它还会发出异步网络请求来请求新容器。 使用 kTAGOpenTypePreferNonDefault 时,如果没有其他容器可用或超出了超时期限,将返回默认容器。

kTAGOpenTypePreferFresh 会尝试在指定的超时期限内从磁盘或网络返回全新容器。如果网络连接不可用和/或超时期限,该方法会返回已保存的容器。

不建议在较长的请求时间可能会明显影响用户体验的位置(例如使用界面标记或显示字符串时)使用 kTAGOpenTypePreferFresh。您还可以随时使用 TAGContainer::refresh 来强制执行网络容器请求。

这两种便捷方法都是非阻塞方法。openContainerWithId:tagManager:openType:timeout: 会返回一个 TAGContainerFuture 对象,其 get 方法会在加载后立即返回一个 TAGContainer(但在此之前会阻塞)。openContainerWithId:tagManager:openType:timeout:notifier: 方法接受一个回调,该回调在容器可用时调用。这两种方法的默认超时期限均为 2.0 秒。

在运行时使用规则评估宏

容器可以在运行时使用规则评估值。规则可以基于设备语言、平台或任何其他宏值等条件。例如,您可以使用规则,根据运行时设备的语言选择本地化的显示字符串。这可以使用以下规则进行配置:

一条规则用于在运行时根据设备语言选择显示字符串:language 等于 es。此规则使用预定义的语言宏和两个字符的 ISO 639-1 语言代码。
图 1:添加规则,以便仅为配置为使用西班牙语的设备启用值集合宏。

然后,您可以为每种语言创建值集合宏,并将此规则添加到每个宏中,并插入相应的语言代码。此容器发布后,您的应用将能够显示本地化的显示字符串,具体取决于运行时用户设备的语言。

请注意,如果您的默认容器需要规则,您必须使用二进制容器文件作为默认容器。

详细了解如何配置规则(帮助中心)。

默认容器二进制文件

需要规则的默认容器应使用二进制容器文件(而不是属性列表文件或 JSON 文件)作为默认容器。二进制容器支持在运行时使用 Google 跟踪代码管理器规则确定宏值,而属性列表或 JSON 文件则不支持。

您可以从 Google 跟踪代码管理器网页界面下载二进制容器文件,并应按照以下命名惯例将二进制容器文件添加到您的主应用软件包:GTM-XXXX,其中文件名表示您的容器 ID。

如果存在属性列表文件和/或 JSON 文件和二进制容器文件,则 SDK 会将二进制容器文件用作默认容器。

使用函数调用宏

函数调用宏是指要设为应用中指定函数的返回值的宏。函数调用宏可用于将运行时值与您的 Google 跟踪代码管理器规则相结合,例如在运行时根据配置的语言和设备货币确定要向用户显示的价格。

如需配置函数调用宏,请执行以下操作:

  1. 在 Google 跟踪代码管理器网页界面中定义函数调用宏。 参数可视需要配置为键值对。
  2. 定义实现 TAGFunctionCallMacroHandler 协议的处理程序:
    // 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. 使用 TAGContainer::registerFunctionCallMacroHandler:forMacro: 和 Google 跟踪代码管理器界面中指定的函数名称注册处理程序:
  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
    

使用函数调用标记

借助函数调用代码,每当有事件被推送到数据层且代码规则的计算结果为 true 时,就会执行预注册函数。

如需配置函数调用代码,请执行以下操作:

  1. 在 Google 跟踪代码管理器网页界面中定义函数调用代码。 参数可视需要配置为键值对。
  2. 实现 TAGFunctionCallTagHandler 协议:
    //
    // 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. 使用在 Google 跟踪代码管理器网页界面中配置的代码名称注册函数调用代码处理程序:
  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
    

设置自定义刷新周期

如果当前容器存在时间超过 12 小时,Google 跟踪代码管理器 SDK 将尝试检索新容器。如需设置自定义容器刷新周期,请使用 NSTimer,如以下示例所示:

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

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

使用日志记录器进行调试

默认情况下,Google 跟踪代码管理器 SDK 会将错误和警告输出到日志中。启用更详细的日志记录有助于调试,并且可以通过实现您自己的 Logger 来实现,如以下示例所示:

// 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

或者,您也可以使用 TagManager::logger::setLogLevel 设置现有 Logger 的 LogLevel,如下例所示:

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