Objective-C 入門指南

本開發人員指南說明如何在行動應用程式中導入 Google 代碼管理工具。

簡介

有了 Google 代碼管理工具,開發人員就能使用 Google 代碼管理工具介面更改行動應用程式的設定值,而不用重新建構應用程式二進位檔並重新提交到應用程式市集。

這適合用來管理應用程式中可能需要變更的任何設定值或標記,包括:

  • 各種 UI 設定和顯示字串
  • 在應用程式中放送的廣告大小、位置或類型
  • 遊戲設定

此外,也可使用規則在執行階段評估設定值,例如啟用下列動態設定:

  • 以螢幕大小決定廣告橫幅大小
  • 使用語言和位置設定 UI 元素

此外,Google 代碼管理工具還能夠在應用程式中動態導入追蹤代碼和像素。開發人員可以將重要事件推送到資料層,稍後再決定要觸發哪些追蹤代碼或像素。TagManager 目前支援下列代碼:

  • Google 行動應用程式分析
  • 自訂函式呼叫代碼

事前準備

使用本入門指南前,您必須備妥以下項目:

如果您是 Google 代碼管理工具新手,建議您先 進一步瞭解容器、巨集和規則 (說明中心),再繼續本指南。

入門課程

本節將引導開發人員完成一般的代碼管理工具工作流程:

  1. 在專案中新增 Google 代碼管理工具 SDK
  2. 設定預設容器值
  3. 開啟容器
  4. 從容器取得設定值
  5. 將事件推送至資料層
  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. 將二進位檔案新增至專案的根目錄和專案中的「輔助檔案」資料夾。

預設檔案名稱應為容器 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. 將值推送到資料層

資料層是一種地圖,可啟用應用程式的執行階段資訊 (例如觸控事件或畫面瀏覽),以便用於容器中的代碼管理工具巨集和代碼。

舉例來說,您可以將畫面瀏覽的相關資訊推送至資料層地圖,在「代碼管理工具」網頁介面中設定代碼來觸發轉換像素,並根據這些畫面瀏覽追蹤呼叫,完全不需要以硬式編碼的方式將代碼導入應用程式。

事件會使用 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

在網頁介面中,您現在可以建立以下規則,建立在每次畫面瀏覽時觸發的代碼 (例如 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;
}

接著,在應用程式屬性清單檔案的網址類型鍵底下,註冊下列網址 ID 和網址配置:

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 會在指定的逾時期間內,嘗試從磁碟或網路傳回新的容器。如果網路連線無法使用,且/或超過逾時時間,這個方法會傳回已儲存的容器。

如果要求時間較長 (例如 UI 旗標或顯示字串) 可能會影響使用者體驗,則不建議使用 kTAGOpenTypePreferFresh。您隨時可以使用 TAGContainer::refresh 強制執行網路容器要求。

這兩種方便方法都是非阻塞式的方法。openContainerWithId:tagManager:openType:timeout: 會傳回 TAGContainerFuture 物件,其 get 方法會在載入後立即傳回 TAGContainer (但在此之前會封鎖)。openContainerWithId:tagManager:openType:timeout:notifier: 方法會採用單一回呼,在容器可用時呼叫。這兩種方法的預設逾時期限皆為 2.0 秒。

使用規則在執行階段評估巨集

容器可在執行階段使用規則評估值。可根據裝置語言、平台或任何其他巨集值等條件建立規則。例如,規則可用於根據執行階段的裝置語言,選取本地化的顯示字串。您可使用以下規則進行設定:

並根據執行階段的裝置語言選取顯示字串:語言等於 e。這項規則使用預先定義的語言巨集和兩個字元 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];