SKAdNetwork 4 インプリメンテーションガイド

SKAdNetwork 4 インプリメンテーションガイド

SKAdNetworkの使用例

SKAdNetwork(SKAN)はAppleのプライバシー重視のアトリビューションフレームワークで、ユーザーのプライバシーを保護しながらiOSアプリインストール広告キャンペーンの測定を可能にします。Server-to-Server (S2S) 実装は、SKAdNetwork データをサーバー間で直接送信することにより、キャンペーンパフォーマンスを検証および追跡する堅牢な方法を提供し、正確なアトリビューションとコンバージョントラッキングを保証します。

このフレームワークはアトリビューションの重要な側面をすべて処理し、Appleの規定する方法でユーザーのプライバシーを保護します。

キーポイント

  • 不正防止機能を組み込んだ、すべてのネットワークからのポストバックの強固な検証と集計
  • ダッシュボード設定による動的なコンバージョン値管理
  • 充実したマーケティングパラメータときめ細かなデータインサイトによるレポート機能の強化
  • デコードされたコンバージョン値と収益追跡のための安全なパートナーポストバックシステム
  • エンリッチイベントとセッショントラッキングによるクライアントサイド実装の包括的な検証
  • 複数のポストバックウィンドウ(0~2日、3~7日、8~35日)にわたる自動タイムスタンプ管理
  • 広告収益化と通常の収益トラッキングの両方を通貨指定でサポート

前提条件

主要コンポーネント

SingularのSKAdNetworkソリューションは以下のコンポーネントで構成されています:

  • SKAdNetworkを実装するクライアントサイドコード
    変換値APIエンドポイントを使用したサーバーサイドのアプローチもあります。
  • すべてのネットワークからのポストバック検証および集計
  • 不正防止:
    • 署名検証とトランザクションIDのデデューピング。
    • 署名されていないパラメータ(コンバージョン値とジオデータ)を検証するためのネットワークとの安全なセットアップ。
  • コンバージョン値の管理: Singularのダッシュボードで、どのインストール後のアクティビティをSKAdNetworkのコンバージョン値にエンコードするかを動的に設定できます。
  • レポーティング:限られたSKAdNetworkキャンペーンIDを変換し、より多くのマーケティングパラメータと粒度でデータを充実させます。
  • パートナーのポストバック: SKAdNetworkのポストバックをイベントと収益にデコードしたコンバージョン値で送信します。

クライアントサイドSKAdNetworkの実装は、主に2つの部分から構成されています:

  1. SKAdNetwork クライアントサイド実装: SKAdNetworkクライアント側の実装:この部分は、アプリをSKAdNetworkに登録し、SKAdNetworkのコンバージョン値をインテリジェントに管理するために重要です。つまり、これを実装することで、SKAdNetwork のアトリビューションと関連するインストール後のアクティビティに基づいてキャンペーンを最適化できるようになります。
  2. サーバーサイドの統合アップデート:このパートは、クライアントサイドの実装を検証し、トラブルシューティングするために重要です。Sessionと Eventエンドポイントを通してSingularに送られるイベントとセッションをSKAdNetworkメタデータでリッチ化することで、Singularはあなたのアプリ側で実装が適切に行われたことを検証することができます。

はじめに


SKAdNetwork クライアントサイド実装

SingularはSKAdNetworkへの登録と変換値の管理をサポートするSKAdNetwork Interface Code Snippetsを提供します。これらのコードサンプルは以下の部分を担当しています:

  1. SKAdNetworkのサポートと登録
  2. 変換値の管理:
    • このコードはSingularのエンドポイントと同期的に通信し、設定されたコンバージョンモデルに基づいて次のコンバージョン値を受け取ります。イベント/セッション/収益を報告し、それに応答して次のコンバージョン値を取得します。この値は、Singularのダッシュボードで測定用に設定されたインストール後のアクティビティを表すエンコードされた数値です。
    • コードはまた、測定期間ごとにSKAdnetworkのメタデータを収集する。メタデータは、検証と次の変換値の計算の両方に使用される:
      • 基礎となるSKAdNetworkフレームワークへの最初の呼び出しタイムスタンプ。
      • 基礎となるSKAdNetworkフレームワークへの最後の呼び出しタイムスタンプ
      • 最後に更新されたポストバック値(CoarseとFineの両方)
      • デバイスによって生成された総収入と総アドモン収入

統合フロー

Screen_Shot_2020-09-16_at_18.59.13.png

上図は、S2S顧客のSKAdNetworkフローを示しています:

  1. まず、アプリ内のコードがSingularサーバーと通信し(SKAdNetwork専用のエンドポイントを通して)、アプリ内で発生したイベント/セッション/収益イベントに基づいて最新のコンバージョン値を同期的に取得し、この値でSKAdNetworkフレームワークを更新します。
  2. 次に、アプリは既存のイベントやセッションをSKAdNetworkのデータでエンリッチします。
  3. アプリが新しい変換値でSKAdNetworkフレームワークを更新し終わり、SKAdNetworkタイマーが切れると、SKAdNetworkポストバックがネットワークに送信されます。
  4. ネットワークはこのポストバックをSingularに転送します(セキュアセットアップまたは通常セットアップ)。
  5. Singularはポストバックを以下のように処理します:
    • 署名の検証
    • 設定された変換モデルに基づいて変換値をデコードします。
    • ネットワーク情報でポストバックをエンリッチします。SKAdNetworkとNetworkのキャンペーンIDを結合することで、パートナーとの統合からデータを収集します。
    • デコードされたポストバックをBIとパートナーに送信します。

重要な注意点として、インストールやデコードされたイベントを含むSKAdNetworkの情報は、既存のデータセットとの混合を避けるために、別のレポート/API/ETLテーブルとポストバックのセットを介してアクセスできるようになります。これは、SKAdNetworkを既存のキャンペーンと並行して測定・テストするために、次の数週間は特に重要です。

SKAdNetwork インターフェース

このヘッダーファイルは、SKAdNetwork(SKAN)統合のためのパブリックインターフェースを定義し、iOSアプリのアトリビューショントラッキング、コンバージョン値の更新、収益管理のためのメソッドを提供します。

SKANSnippet.h コードサンプル

アトリビューション登録

最初のアプリ起動時にSKANのアトリビューショントラッキングを初期化し、初期コンバージョン値を0に設定し、ベースラインタイムスタンプを確立します。基礎となるApple APIメソッドは、デバイスにそのアプリのアトリビューションデータがある場合に通知を生成します。

Objective-C
+ (void)registerAppForAdNetworkAttribution;

コンバージョン値の管理

  • コンバージョン値は、以下のメソッドによって取得されたデバイスのインストール後のアクティビティと、ユーザーが動的に設定できる選択されたコンバージョンモデルに基づいて計算されます。
  • このセクションのメソッドは、選択されたコンバージョンモデルと報告されたインストール後のアクティビティに従って、Singularのエンドポイントから次のコンバージョン値を取得します(上記のドキュメントを参照)。
  • 以下のメソッドは、以下のインストール後のアクティビティに基づいてコンバージョン値を更新します:
    • セッション:SKAdNetworkでのユーザーのリテンション測定に重要です。
    • コンバージョンイベント:SKAdNetworkでのインストール後のコンバージョンイベント測定に重要です。
    • 収益イベント:SKAdNetwork での収益測定に重要。

セッショントラッキング

リテンションとコホート分析のためのセッションベースのトラッキングを管理します。

Objective-C
+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

イベントトラッキング

Singularにデータを送信する前にコンバージョンイベントトラッキングを処理し、イベントコンテキストに基づいてコンバージョン値を更新します。

Objective-C
+ (void)updateConversionValuesAsync:(NSString *)eventName 
                withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

収益管理

収益イベントをトラッキングし、広告収益と通常の収益を別々に管理します。

Objective-C
+ (void)updateRevenue:(double)amount 
          andCurrency:(NSString *)currency 
     isAdMonetization:(BOOL)admon;

データ検索

以下を含む包括的なSKANデータを返します:

  • 現在および以前の詳細なコンバージョン値
  • 異なるポストバックウィンドウにわたる粗い値
  • ウィンドウロックのタイムスタンプ
  • 通貨別の収益トラッキング
  • 広告収益と通常の収益を別々にトラッキング
Objective-C
+ (NSDictionary *)getSkanDetails;

実装上の注意

  • メインスレッドのブロックを防ぐため、メソッドは非同期パターンを使用します。
  • 収益トラッキングはコンバージョン値の更新に先行する必要がある
  • きめ細かい(0-63)コンバージョン値と粗い(低/中/高)コンバージョン値の両方をサポートします。
  • 異なるポストバックウィンドウに対して別々のトラッキングを維持します。
  • 完了ハンドラによる包括的なエラー処理の実装

SKANSnippet.h インターフェースコード

Objective-C
//SKANSnippet.h

#import <Foundation/Foundation.h>

@interface SKANSnippet : NSObject

// Register for SKAdNetwork attribution.

// You should call this method as soon as possible once the app is launched for the first time.

// This function sets the conversion value to be 0 and updates the timestamp for additional processing.

+ (void)registerAppForAdNetworkAttribution;

// To track retention and cohorts you need to call this method for each app open.

// It reports the session details and updates the conversion value due to this session if needed.

// The callback passed to the method is optional, you can use it to run code once the conversion value is updated.

+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

// To track conversion events with SKAdNetwork you need to call this method after each event and before this event is sent to Singular.

// It reports the event details and updates the conversion value due to this event if needed.

// The callback passed to the method is optional, you can use it to run code once the conversion value is updated.

+ (void)updateConversionValuesAsync:(NSString *)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler;

// To track revenue with SKAdNetwork you need to call this method before every revenue event.

// It will update the total revenue, so when you call 'updateConversionValuesAsync', the new conversion value will be determined according to the total amout of revenue.

// Note:

// 1. Call this method before calling 'updateConversionValuesAsync' to make sure that revenue is updated.

// 2. In case of retrying an event, avoid calling this method so the same revenue will not be counted twice.

+ (void)updateRevenue:(double)amount andCurrency:(NSString *)currency isAdMonetization:(BOOL)admon;

// Gets current fine, coarse, window locked values. saved in the dictionary under "fineValue", "coarseValue", "windowLock".

// In addition, contains all other relevant values for SKAN purposes.

// e.g. 

// {

//    "skan_current_conversion_value": 3,

//    "prev_fine_value": 2,

//    "skan_first_call_to_skadnetwork_timestamp": 167890942,

//    "skan_last_call_to_skadnetwork_timestamp": 167831134,

//    "skan_total_revenue_by_currency": { "USD": 1.2 },

//    "skan_total_admon_revenue_by_currency": { "USD": 0.8 },

//    "p0_coarse": 0,

//    "p1_coarse": 1,

//    "p2_coarse": nil,

//    "p0_window_lock": 167890942,

//    "p1_window_lock": nil,

//    "p2_window_lock": nil,

//    "p0_prev_coarse_value": 0,

//    "p1_prev_coarse_value": 0,

//    "p2_prev_coarse_value": nil,

//    "p0_total_iap_revenue": nil,

//    "p1_total_iap_revenue": nil,

//    "p2_total_iap_revenue": nil,

//    "p0_total_admon_revenue": nil,

//    "p1_total_admon_revenue": nil,

//    "p2_total_admon_revenue": nil

// }

+ (NSDictionary *)getSkanDetails;

 @end

SKAdNetworkインターフェースの実装

このコードは、iOSアプリ用のAppleのSKAdNetwork(SKAN)インターフェースを実装し、複数のポストバックウィンドウにわたってアトリビューショントラッキング、コンバージョン値、収益レポートを管理します。

SKANSnippet.m コードサンプル

定数と設定

この実装では、ユーザーのアクティビティとコンバージョンをトラッキングするために、3つの異なるポストバックウィンドウを定義しています。

Objective-C
static NSInteger firstSkan4WindowInSec = 3600 * 24 * 2;  // 48 hours

static NSInteger secondSkan4WindowInSec = 3600 * 24 * 7;  // 7 days

static NSInteger thirdSkan4WindowInSec = 3600 * 24 * 35; // 35 days

主な機能

  • アトリビューション登録:アプリのアトリビューションの初期設定と初回のコンバージョン値のトラッキングを処理します。
  • コンバージョン値の管理:複数のポストバックウィンドウでコンバージョン値の更新とトラッキングを行います。
  • 収益トラッキング広告収益化と通常の収益イベントを個別にトラッキングします。
  • データの永続化:NSUserDefaultsを使用して、アプリのセッションをまたいでSKAN関連データを保存します。
  • スレッドの安全性:ネットワーク呼び出し時のスレッドセーフな操作のためにNSLockを実装します。

データ保存構造

  • 細かい変換値(0~63)
  • 粗い値(低/中/高)
  • 通貨別の収益追跡
  • ポストバックウィンドウのタイムスタンプ管理
  • 細かい変換値と粗い変換値の両方について、以前の値をトラッキング

プライバシーへの配慮

  • iOS 15.4+およびiOS 16.1+固有の機能を実装
  • Appleのプライバシーガイドラインに従ってポストバックコンバージョン値の更新を処理します。
  • 正確なアトリビューションを保証するために、異なる収益タイプに対して個別のトラッキングを維持します。

テクニカルノート

  • ネットワークコールと値の更新に非同期オペレーションを使用
  • コンバージョン値のエラー処理と検証を実装しています。
  • 従来のコンバージョン値と粗いコンバージョン値の両方のトラッキングをサポート
  • 異なる期間と要件を持つ複数のポストバックウィンドウを管理します。

S2S統合のアップデート(推奨)

以下のSKAdNetworkメタデータを使用して、S2Sインテグレーションを更新してください。

このメタデータは、セッション通知エンドポイントおよびイベント通知エンドポイントを経由してSingularに報告される全てのセッションおよび全てのイベントに転送されます。このデータはSKAdNetwork実装の検証に使用されます。

メタデータの構造

変換値

Data Retrievalメソッドを使用して、メタデータの辞書を抽出し、それをサーバーに転送して、SessionおよびEventエンドポイントAPIリクエストのクエリパラメータとして追加します。

Objective-C
NSDictionary *values = [SKANSnippet getSkanDetails];

SKANSnippet.m 実装コード

Objective-C
//  SKANSnippet.m


#import "SKANSnippet.h"
#import <StoreKit/SKAdNetwork.h>
#import <UIKit/UIKit.h>

#define SESSION_EVENT_NAME @"__SESSION__"
#define SINGULAR_API_URL @"https://sdk-api-v1.singular.net/api/v2/conversion_value"

// SKAN Keys for NSUserDefaults persistency and requests

#define CONVERSION_VALUE_KEY @"skan_current_conversion_value"
#define FIRST_SKAN_CALL_TIMESTAMP @"skan_first_call_to_skadnetwork_timestamp"
#define LAST_SKAN_CALL_TIMESTAMP @"skan_last_call_to_skadnetwork_timestamp"
#define TOTAL_REVENUE_BY_CURRENCY @"skan_total_revenue_by_currency"
#define TOTAL_ADMON_REVENUE_BY_CURRNECY @"skan_total_admon_revenue_by_currency"
#define SKAN_UPDATED_CONVERSION_VALUE @"conversion_value"
#define SKAN_UPDATED_COARSE_VALUE @"skan_updated_coarse_value"
#define SKAN_UPDATED_LOCK_WINDOW_VALUE @"skan_updated_lock_window_value"

#define P0_COARSE @"p0_coarse"
#define P1_COARSE @"p1_coarse"
#define P2_COARSE @"p2_coarse"
#define P0_WINDOW_LOCK_TS @"p0_window_lock"
#define P1_WINDOW_LOCK_TS @"p1_window_lock"
#define P2_WINDOW_LOCK_TS @"p2_window_lock"

#define P0_PREV_FINE_VALUE @"prev_fine_value"
#define P0_PREV_COARSE_VALUE @"p0_prev_coarse_value"
#define P1_PREV_COARSE_VALUE @"p1_prev_coarse_value"
#define P2_PREV_COARSE_VALUE @"p2_prev_coarse_value"

#define TOTAL_REVENUE_P0 @"p0_total_iap_revenue"
#define TOTAL_REVENUE_P1 @"p1_total_iap_revenue"
#define TOTAL_REVENUE_P2 @"p2_total_iap_revenue"
#define TOTAL_ADMON_REVENUE_P0 @"p0_total_admon_revenue"
#define TOTAL_ADMON_REVENUE_P1 @"p1_total_admon_revenue"
#define TOTAL_ADMON_REVENUE_P2 @"p2_total_admon_revenue"

@implementation SKANSnippet

static NSInteger firstSkan4WindowInSec = 3600 * 24 * 2; //48 hours in sec
static NSInteger secondSkan4WindowInSec = 3600 * 24 * 7;
static NSInteger thirdSkan4WindowInSec = 3600 * 24 * 35;

static NSLock *lockObject;

+ (void)registerAppForAdNetworkAttribution {
    if ([SKANSnippet getFirstSkanCallTimestamp] != 0) {
        return;
    }
    
    if (@available(iOS 15.4, *)) {
        [SKAdNetwork updatePostbackConversionValue:0 completionHandler:nil];
        [SKANSnippet setFirstSkanCallTimestamp];
        [SKANSnippet setLastSkanCallTimestamp];
        [SKANSnippet valuesHasBeenUpdated:@(0) coarseValue:nil lockWindow:NO];
    } 
}

+ (void)updateConversionValuesAsync:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler {
    [SKANSnippet updateConversionValuesAsync:SESSION_EVENT_NAME withCompletionHandler:handler];
}

+ (void)updateConversionValuesAsync:(NSString *)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError *))handler {
        if ([SKANSnippet isSkanWindowOver]) {
            return;
        }
        
        [SKANSnippet getConversionValueFromServer:eventName withCompletionHandler:handler];
}

+ (void)updateRevenue:(double)amount andCurrency:(NSString *)currency isAdMonetization:(BOOL)admon {
    // Update total revenues

    if (amount == 0 || !currency ) {
        return;
    }
    
    [SKANSnippet addToTotalRevenue:@(amount) withCurrency:currency isAdmon:admon];
}

+ (NSDictionary *)getSkanDetails {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *res = [NSMutableDictionary dictionary];
    //current fine
    [res setValue:[[userDefaults valueForKey:CONVERSION_VALUE_KEY] stringValue] forKey:CONVERSION_VALUE_KEY];
    //prev fine
    [res setValue:[[userDefaults valueForKey:P0_PREV_FINE_VALUE] stringValue] forKey:P0_PREV_FINE_VALUE];
    //current coarse
    [res setValue:[[userDefaults valueForKey:P0_COARSE] stringValue] forKey:P0_COARSE];
    [res setValue:[[userDefaults valueForKey:P1_COARSE] stringValue] forKey:P1_COARSE];
    [res setValue:[[userDefaults valueForKey:P2_COARSE] stringValue] forKey:P2_COARSE];
    //prev coarse
    [res setValue:[[userDefaults valueForKey:P0_PREV_COARSE_VALUE] stringValue] forKey:P0_PREV_COARSE_VALUE];
    [res setValue:[[userDefaults valueForKey:P1_PREV_COARSE_VALUE] stringValue] forKey:P1_PREV_COARSE_VALUE];
    [res setValue:[[userDefaults valueForKey:P2_PREV_COARSE_VALUE] stringValue] forKey:P2_PREV_COARSE_VALUE];
    //lock windows ts
    [res setValue:[[userDefaults valueForKey:P0_WINDOW_LOCK_TS] stringValue] forKey:P0_WINDOW_LOCK_TS];
    [res setValue:[[userDefaults valueForKey:P1_WINDOW_LOCK_TS] stringValue] forKey:P1_WINDOW_LOCK_TS];
    [res setValue:[[userDefaults valueForKey:P2_WINDOW_LOCK_TS] stringValue] forKey:P2_WINDOW_LOCK_TS];
    //total revenues
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_BY_CURRENCY] forKey:TOTAL_REVENUE_BY_CURRENCY];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_BY_CURRNECY] forKey:TOTAL_ADMON_REVENUE_BY_CURRNECY];
    //revenue per window
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P0] forKey:TOTAL_REVENUE_P0];
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P1] forKey:TOTAL_REVENUE_P1];
    [res setValue:[userDefaults valueForKey:TOTAL_REVENUE_P2] forKey:TOTAL_REVENUE_P2];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P0] forKey:TOTAL_ADMON_REVENUE_P0];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P1] forKey:TOTAL_ADMON_REVENUE_P1];
    [res setValue:[userDefaults valueForKey:TOTAL_ADMON_REVENUE_P2] forKey:TOTAL_ADMON_REVENUE_P2];
    //skan TS
    [res setValue:[[userDefaults valueForKey:LAST_SKAN_CALL_TIMESTAMP] stringValue] forKey:LAST_SKAN_CALL_TIMESTAMP];
    [res setValue:[[userDefaults valueForKey:FIRST_SKAN_CALL_TIMESTAMP] stringValue] forKey:FIRST_SKAN_CALL_TIMESTAMP];
    
    return res;
}


#pragma mark - internal
+ (BOOL)validateValues:(NSNumber *)conversionValue coarse:(NSNumber *)coarseValue{
    if ([conversionValue intValue] < 0 || 63 < [conversionValue intValue]) {
        return NO;
    }
    
    if (coarseValue) {
        if ([coarseValue intValue] > 2 || [coarseValue intValue] < 0) {
            return NO;
        }
    }
    
    return YES;
}

+ (NSURLComponents *)prepareQueryParams:(NSString *)bundleIdentifier eventName:(NSString *)eventName {
    NSURLComponents *components = [NSURLComponents componentsWithString:SINGULAR_API_URL];
    
    NSString *API_KEY = @"YOUR API KEY";
    NSString *APP_VERSION = @"YOUR APP VERSION";
    NSString *IDFV = @"IDFV";
    NSString *IDFA = @"IDFA";
    
    NSMutableArray *queryItems = [@[
        [NSURLQueryItem queryItemWithName:@"a" value:API_KEY],
        [NSURLQueryItem queryItemWithName:@"v" value:[[UIDevice currentDevice] systemVersion]],
        [NSURLQueryItem queryItemWithName:@"i" value:bundleIdentifier],
        [NSURLQueryItem queryItemWithName:@"app_v" value:APP_VERSION],
        [NSURLQueryItem queryItemWithName:@"n" value:eventName],
        [NSURLQueryItem queryItemWithName:@"p" value:@"iOS"],
        [NSURLQueryItem queryItemWithName:@"idfv" value:IDFV],
        [NSURLQueryItem queryItemWithName:@"idfa" value:IDFA]
    ] mutableCopy];
    
    NSDictionary *skanValues = [SKANSnippet getSkanDetails];
    [skanValues enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            [queryItems addObject:[NSURLQueryItem queryItemWithName:key value:[SKANSnippet dictionaryToJsonString:obj]]];
        } else {
            [queryItems addObject:[NSURLQueryItem queryItemWithName:key value:obj]];
        }
    }];
    
    components.queryItems = queryItems;
    
    return components;
}

+ (void)getConversionValueFromServer:(NSString*)eventName withCompletionHandler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError*))handler {
    if (!lockObject) {
        lockObject = [NSLock new];
    }
    
    @try {
        // Making the lock async so it will not freeze the calling thread

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
            
            [lockObject lock];
            NSString *bundleIdentifier = @"YOUR BUNDLE IDENTIFIER";
            NSURLComponents *components = [SKANSnippet prepareQueryParams:bundleIdentifier eventName:eventName];
            
            [[[NSURLSession sharedSession] dataTaskWithURL:components.URL
                                         completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                if (error) {
                    [lockObject unlock];
                    if (handler) {
                        handler(nil, nil, NO, error);
                    }
                    
                    return;
                }
                
                NSDictionary *parsedResponse = [SKANSnippet jsonDataToDictionary:data];
                
                if (!parsedResponse) {
                    [lockObject unlock];
                    if (handler) {
                        handler(nil,nil, NO, [NSError errorWithDomain:bundleIdentifier
                                                                 code:0
                                                             userInfo:@{NSLocalizedDescriptionKey:@"Failed parsing server response"}]);
                    }
                    
                    return;
                }
                
                NSNumber *conversionValue = [parsedResponse objectForKey:SKAN_UPDATED_CONVERSION_VALUE];
                NSNumber *coarseValue = [parsedResponse objectForKey:SKAN_UPDATED_COARSE_VALUE];
                BOOL lockWindow = [[parsedResponse objectForKey:SKAN_UPDATED_LOCK_WINDOW_VALUE] boolValue];
                
                
                if (!conversionValue) {
                    [lockObject unlock];
                    NSString *status = [parsedResponse objectForKey:@"status"];
                    
                    if (!status || ![status isEqualToString:@"ok"]) {
                        if (handler) {
                            NSString *reason = [parsedResponse objectForKey:@"reason"];
                            if (!reason) {
                                reason = @"Got error from server";
                            }
                            
                            handler(nil, nil, NO, [NSError errorWithDomain:bundleIdentifier
                                                                      code:0
                                                                  userInfo:@{NSLocalizedDescriptionKey:reason}]);
                        }
                    }
                    
                    return;
                }
                
                if(![SKANSnippet validateValues:conversionValue coarse:coarseValue]) {
                    if (handler) {
                        handler(nil,nil, NO, [NSError errorWithDomain:bundleIdentifier
                                                                 code:0
                                                             userInfo:@{NSLocalizedDescriptionKey:@"Illegal values recieved"}]);
                    }
                    
                    return;
                }
                
                if (![SKANSnippet getFirstSkanCallTimestamp]) {
                    [SKANSnippet setFirstSkanCallTimestamp];
                }
                
                [SKANSnippet setConversionValues:conversionValue coarseValue:coarseValue lockWindow:lockWindow handler:handler];
                
                [lockObject unlock];
            }] resume];
        });
    } @catch (id exception) {
        NSLog(@"%@", exception);
    }
}

+ (void)setFirstSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp] forKey:FIRST_SKAN_CALL_TIMESTAMP];
    [userDefaults synchronize];
}

+ (void)setLastSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp] forKey:LAST_SKAN_CALL_TIMESTAMP];
    [userDefaults synchronize];
}

+ (NSString*)dictionaryToJsonString:(NSDictionary*)dictionary {
    if (!dictionary || [dictionary count] == 0){
        return @"{}";
    }
    
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
                                                       options:0
                                                         error:&error];
    
    if (error || !jsonData) {
        return @"{}";
    }
    
    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

+ (NSInteger)getFirstSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    return [userDefaults integerForKey:FIRST_SKAN_CALL_TIMESTAMP];
}

+ (NSInteger)getLastSkanCallTimestamp {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    return [userDefaults integerForKey:LAST_SKAN_CALL_TIMESTAMP];
}

+ (NSInteger)getCurrentUnixTimestamp {
    return [[NSDate date] timeIntervalSince1970];
}

+ (void)setConversionValues:(NSNumber *)conversionValue coarseValue:(NSNumber *)coarse lockWindow:(BOOL)lockWindow handler:(void(^)(NSNumber *, NSNumber *, BOOL, NSError*))handler {
    @try {
        __block void(^skanResultHandler)(NSError * _Nullable error) = ^(NSError * _Nullable error) {
            if (handler) {
                if (error) {
                    handler(nil, nil, NO, error);
                } else {
                    handler(conversionValue, coarse, lockWindow, nil);
                }
            }
            
            [SKANSnippet valuesHasBeenUpdated:conversionValue coarseValue:coarse lockWindow:lockWindow];
        };
        
        if (@available(iOS 16.1, *)) {
            [SKAdNetwork updatePostbackConversionValue:[conversionValue integerValue] coarseValue:[SKANSnippet resolveCoarseValueFrom:coarse] lockWindow:lockWindow completionHandler:^(NSError * _Nullable error) {
                skanResultHandler(error);
            }];
        } else {
            if (@available(iOS 15.4, *)) {
                [SKAdNetwork updatePostbackConversionValue:[conversionValue integerValue] completionHandler:^(NSError * _Nullable error) {
                    skanResultHandler(error);
                }];
            }
        }
    } @catch (id exception) {
        NSLog(@"%@", exception);
    }
}

+ (NSNumber *)getConversionValue {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    if (![userDefaults objectForKey:CONVERSION_VALUE_KEY]) {
        return @(0);
    }
    
    return @([userDefaults integerForKey:CONVERSION_VALUE_KEY]);
}

+ (NSDictionary*)jsonDataToDictionary:(NSData*)jsonData {
    if (!jsonData) {
        return nil;
    }
    
    NSError *error;
    NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:jsonData
                                                                options:kNilOptions error:&error];
    
    if (error || !parsedData) {
        return nil;
    }
    
    return parsedData;
}

+ (NSInteger)getCurrentSkanWindow {
    NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getFirstSkanCallTimestamp];
    if (timeDiff < firstSkan4WindowInSec) { return 0; }
    if (timeDiff < secondSkan4WindowInSec) { return 1; }
    if (timeDiff < thirdSkan4WindowInSec) { return 2; }
    
    return -1;
}

// persist updated conversion values based on the active skan window.

+ (void)valuesHasBeenUpdated:(NSNumber *)fineValue coarseValue:(NSNumber *)coarseValue lockWindow:(BOOL)lockWindow {
    NSNumber *currentPersistedFineValue;
    NSNumber *currentPersistedCoarseValue;
    NSInteger window = [SKANSnippet getCurrentSkanWindow];
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
        
    switch (window) {
    case 0:
        currentPersistedFineValue = [userDefaults objectForKey:CONVERSION_VALUE_KEY];
        currentPersistedCoarseValue = [userDefaults objectForKey:P0_COARSE];
        [userDefaults setValue:fineValue forKey:CONVERSION_VALUE_KEY];
        [userDefaults setValue:currentPersistedFineValue forKey:P0_PREV_FINE_VALUE];
        [userDefaults setValue:coarseValue forKey:P0_COARSE];
        [userDefaults setValue:currentPersistedCoarseValue forKey:P0_PREV_COARSE_VALUE];
            
        if (lockWindow) {
            [userDefaults setObject:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P0_WINDOW_LOCK_TS];
        }
            
        break;
    
    case 1:
        currentPersistedCoarseValue = [userDefaults objectForKey:P1_COARSE];
        [userDefaults setValue:coarseValue forKey:P1_COARSE];
        [userDefaults setValue:currentPersistedCoarseValue forKey:P1_PREV_COARSE_VALUE];
            
        if (lockWindow) {
            [userDefaults setObject:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P1_WINDOW_LOCK_TS];
        }
            
        break;
    
    case 2:
        currentPersistedCoarseValue = [userDefaults objectForKey:P2_COARSE];
        [userDefaults setValue:coarseValue forKey:P2_COARSE];
        [userDefaults setValue:currentPersistedCoarseValue forKey:P2_PREV_COARSE_VALUE];
            
        if (lockWindow) {
            [userDefaults setValue:@([SKANSnippet getCurrentUnixTimestamp]) forKey:P2_WINDOW_LOCK_TS];
        }
            
        break;
    }
    
    [SKANSnippet setLastSkanCallTimestamp];
}

+ (BOOL)isSkanWindowOver {
    NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] - [SKANSnippet getFirstSkanCallTimestamp];
    return thirdSkan4WindowInSec <= timeDiff;
}

// Revenues are being accumulated and saved by Ad monetization and non ad monetization events, total sum and break down by skan windows.

+ (void)addToTotalRevenue:(NSNumber *)newRevenue withCurrency:(NSString *)currency isAdmon:(BOOL)isAdmon  {
    NSString *key = isAdmon ? TOTAL_ADMON_REVENUE_BY_CURRNECY : TOTAL_REVENUE_BY_CURRENCY;
    [SKANSnippet addToTotalRevenue:newRevenue withCurrency:currency forKey:key];
        
    NSInteger window = [SKANSnippet getCurrentSkanWindow];
    switch (window) {
        case 0:
            key = isAdmon ? TOTAL_ADMON_REVENUE_P0 : TOTAL_REVENUE_P0 ;
            break;
        case 1:
            key = isAdmon ? TOTAL_ADMON_REVENUE_P1 : TOTAL_REVENUE_P1 ;
            break;
            
        case 2:
            key = isAdmon ? TOTAL_ADMON_REVENUE_P2 : TOTAL_REVENUE_P2 ;
            break;
        case -1:
            key = nil;
            return;
    }
    
    [SKANSnippet addToTotalRevenue:newRevenue withCurrency:currency forKey:key];
}

// Coarse value is being sent on requests and responses as an Int and being translated into the system defined coarse value upon API execution.

+ (NSString *)resolveCoarseValueFrom:(NSNumber *)value {
    if(@available(iOS 16.1, *)) {
        if (!value) {
            return nil;
        }
        
        switch ([value integerValue]) {
            case 0:
                return SKAdNetworkCoarseConversionValueLow;
            case 1:
                return SKAdNetworkCoarseConversionValueMedium;
            case 2:
                return SKAdNetworkCoarseConversionValueHigh;
            default:
                return nil;
        }
    }
    
    return nil;
}

+ (NSDictionary*)getTotalRevenue:(NSString *)revenueKey {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    if (![userDefaults objectForKey:revenueKey]){
        [userDefaults setObject:[NSDictionary dictionary] forKey:revenueKey];
    }
    
    return [userDefaults objectForKey:revenueKey];
}

+ (void)addToTotalRevenue:(NSNumber *)newRevenue withCurrency:(NSString *)currency forKey:(NSString *)revenueKey {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *revenues = [[SKANSnippet getTotalRevenue:revenueKey] mutableCopy];
    NSNumber *currentRevenue = 0;

    if ([revenues objectForKey:currency]) {
        currentRevenue = [revenues objectForKey:currency];
    }

    currentRevenue = @([currentRevenue floatValue] + [newRevenue floatValue]);
    [revenues setObject:currentRevenue forKey:currency];
    [userDefaults setObject:revenues forKey:revenueKey];
    [userDefaults synchronize];
}

@end

アプリのライフサイクルフローの例

このコードは、iOSアプリのライフサイクルにおけるSKAdNetwork(SKAN)アトリビューションの主要な統合ポイントを示し、アプリの起動、セッション、イベント、収益追跡を処理します。

実装上の注意

  • このコードでは、メインスレッドのブロックを防ぐため、コンバージョン値の更新に非同期メソッドを使用しています。
  • すべてのSKAN関連データは、サーバーに送信される前に辞書形式で収集されます。
  • 実装は、Appleのプライバシー優先のアプローチに従いつつ、本質的なアトリビューション追跡を可能にしています。
  • 収益追跡には、正確な財務報告のための貨幣価値と通貨指定の両方が含まれます。

アプリの初回起動

このコードはアトリビューショントラッキングのためにアプリをSKAdNetworkに登録し、初期セッションデータをSingularのエンドポイントに送信します。アトリビューショントラッキングを確立するために、最初のアプリ起動時にのみ実行されます。

これは、アトリビューショントラッキングを確立するための最初のアプリ起動時にのみ実行されます。

Objective-C
[SKANSnippet registerAppForAdNetworkAttribution];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendSessionToServer:skanValues] //to Singular launch EP 

セッション管理

このセクションでは、各セッション後にコンバージョン値を更新し、ユーザーエンゲージメントを追跡するために更新されたSKANの詳細を送信します。

Objective-C
[SKANSnippet updateConversionValuesAsync:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendSessionToServer:skanValues]

イベントトラッキング

このコードは、コンバージョン値を更新し、イベントデータをSingularのイベントエンドポイントに送信することで、収益以外のイベントを処理します。

Objective-C
[SKANSnippet updateConversionValuesAsync:@"event_name" withCompletionHandler:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendEventToServer:skanValues eventName:@"event_name"]

収益トラッキング

このセクションでは、通貨による収益額と関連するコンバージョン値の両方を更新することで、収益イベントを管理します。そのデータは購入関連のアクティビティをトラッキングするために Singular のイベントエンドポイントに送られます。

Objective-C
[SKANSnippet updateRevenue:15.53 andCurrency:@"KRW"];
[SKANSnippet updateConversionValuesAsync:@"revenue_event_name" withCompletionHandler:handler];
NSDictionary *skanValues = [SKANSnippet getSkanDetails];
[self sendEventToServer:skanValues eventName:@"revenue_event_name"]

コンバージョン値API

SKAdNetworkのコンバージョン値は、2つのメソッドで報告することができます:

  1. クライアント側で直接SKAdNetworkインターフェイスを実装する - 上記参照
  2. コンバージョンバリューAPIエンドポイントを使用したサーバーサイドの統合

どちらの方法でも同じデータフローとレポート整合性が維持されるため、お客様の技術アーキテクチャに最適な実装を選択することができます。Conversion Value API Endpointは、クライアント側インターフェースと同じパラメータを受け付けるため、一貫したアトリビューション追跡が可能です。

コンバージョンバリューAPIリファレンス

内容


変換値APIエンドポイント

HTTPメソッドと変換値エンドポイント

GET https://sdk-api-v1.singular.net/api/v2/conversion_value

必須パラメータ

次の表は、サーバーからConversion APIをサポートするための必須パラメーターとオプションパラメーターの一覧です。記載されているパラメータはすべてクエリパラメータです。

必須パラメータ
APIキー
パラメータ 説明
a
string

aパラメータはSingular SDK Keyを指定する。

SDKキーはSingular UIのメインメニューのDeveloper Toolsから取得してください。

注意:データが拒否されるため、レポートAPIキーは使用しないでください。

 

値の例
sdkKey_afdadsf7asf56
デバイス識別子パラメータ
パラメータ 説明
idfa
string

idfaパラメータは、広告主がユーザーのアクション(広告のクリックやアプリのインストールなど)を追跡し、特定のキャンペーンに帰属させることで、正確な広告ターゲティングとキャンペーンの最適化を可能にする広告主向け識別子(IDFA)を指定します。

iOS 14.5以降、アプリがIDFAにアクセスする前に、ユーザーはApp Tracking Transparency(ATT)フレームワークを介してオプトインする必要があります。ユーザーがIDFAにオプトインしない場合、IDFAは利用できなくなり、トラッキング機能が制限されます。

  • IDFAが利用できない場合は、リクエストからパラメータを省略してください。
  • リクエストにNULLや空文字列を渡さないでください。
  • IDFA 識別子の取得方法

 

値の例
DFC5A647-9043-4699-B2A5-76F03A97064B
パラメータ 説明
idfv
string

idfvパラメータは、IDFV(Identifier for Vendors)を指定します。IDFVは、Appleがデバイスに割り当てる一意の識別子で、特定のベンダーまたはデベロッパーに固有のものです。これは、特定のベンダーまたはデベロッパーに固有のもので、特定のデバイス上の同じベンダーのすべてのアプリで一貫性を維持し、ベンダーがユーザーを個人的に識別することなく、そのアプリのエコシステム全体でユーザーの行動やインタラクションを追跡できるようにします。

  • IDFVは、ATTステータスやIDFAの有無にかかわらず、すべてのリクエストで要求されます。
  • IDFV識別子の取得方法

 

値の例
21DB6612-09B3-4ECC-84AC-B353B0AF1334
デバイスパラメータ
パラメータ 説明
p
string

pパラメータはアプリのプラットフォームを指定する。このAPIはiOSでのみ使用されるため、この値はiOSでなければならない。

 

値の例
iOS
パラメータ 説明
v
string

vパラメータは、セッション時のデバイスのOSバージョンを指定する。

 

値の例
16.1
アプリケーション・パラメータ
パラメータ 説明
i
string

iパラメータはアプリケーション識別子を指定する。

これはiOSアプリケーションのバンドルIDです。(大文字と小文字は区別されます)

値の例
com.singular.app
パラメータ 説明
app_v
string

app_vパラメータはアプリケーション・バージョンを指定する。

 

1.2.3
イベント・パラメータ
パラメータ 説明
n
string

nパラメータは追跡されるイベントの名前を指定する。

  • 制限:最大32 ASCII文字
  • セッションには、イベント名を使用します:
    __SESSION__
  • セッション以外のイベントには、イベントAPIエンドポイントを介してSingularに送信された同じイベント名とケーシングを使用してください。

 

値の例
sng_add_to_cart
変換値パラメータ
パラメータ 説明
skan_current_conversion_value

サポートされているプラットフォーム

  • iOS 15.4+
int

前回のセッション/イベント通知時の最新のSKAdNetwork変換値。0~63の整数値。

 

値の例

7
パラメータ 説明
p1_coarse

サポートされているプラットフォーム

  • iOS 16.1+
int

postback_sequence 1の最新のSKAdNetwork粗変換値(前回のセッション/イベント通知時)。これは(0-2)の間の整数です。

 

値の例

0
パラメータ 説明
p2_coarse

サポートされるプラットフォーム

  • iOS 16.1+
int

postback_sequence 2の最新のSKAdNetwork粗変換値(前回のセッション/イベント通知時)。(0-2)の整数値。

 

値の例

1
収益トラッキングパラメータ
パラメータ 説明
skan_total_revenue_by_currency

サポートされるプラットフォーム

  • iOS 15.4+
JSON URL-encoded string

IAPまたはAll Revenueモデルを使用している場合は必須。広告収益以外の、デバイスが生成したIAP収益の現在の集計。

{
   "USD":9.99
}

 

値の例

%7B%22USD%22%3A9.99%7D
パラメータ 説明
skan_total_admon_revenue_by_currency

サポートされるプラットフォーム

  • iOS 15.4以上
JSON URL-encoded string

AdmonまたはAll Revenue Conversion Modelsを使用する場合は必須。デバイスによって生成された広告収益化の現在の集計。

{
   "USD":1.2
}

 

値の例

%7B%22USD%22%3A5%7D
タイムスタンプパラメータ
パラメータ 説明
skan_first_call_to_skadnetwork_timestamp

サポートされているプラットフォーム

  • iOS 15.4+
int

基礎となるSKAdNetwork APIへの最初の呼び出しのUnixタイムスタンプ。

 

値の例

1483228800
パラメータ 説明
skan_last_call_to_skadnetwork_timestamp

サポートされているプラットフォーム

  • iOS 15.4+
int

このセッション通知の時点で、基礎となるSKAdNetwork APIへの最新の呼び出しのUnixタイムスタンプ。

 

値の例

1483228800

リクエストボディ

このメソッドを呼び出すときは、リクエストボディを指定しないでください。リクエストはクエリパラメータ付きのGETメソッドを使用して送信されなければならない。

 

リクエストの例

以下のコードサンプルは、サポートされているすべてのパラメータを表しているわけではありません。リクエストを実装する際には、上記のように必要なパラメータをすべて含めるようにし、本番インスタンスからデータを送信する前に正しい値が渡されていることを確認してください。開発とテストのために、一意な `i` パラメータ(アプリケーション識別子)を使用することを推奨します。

 

PYTHON CURL HTTP JAVA

PYTHON

import requests

params = {
    'a': 'sdk_key_here',
    'p': 'iOS',
    'i': 'com.singular.app',
    'v': '16.1',
    'idfa': 'DFC5A647-9043-4699-B2A5-76F03A97064B',
    'idfv': '21DB6612-09B3-4ECC-84AC-B353B0AF1334',
    'n': '__SESSION__',
    'app_v': '1.2.3',
    'skan_current_conversion_value': 7,
    'p1_coarse': 0,
    'p2_coarse': 1,
    'skan_total_revenue_by_currency': {"USD":9.99},
    'skan_total_admon_revenue_by_currency': {"USD":1.2},
    'skan_first_call_to_skadnetwork_timestamp': 1510090877,
    'skan_last_call_to_skadnetwork_timestamp': 1510090877
}

response = requests.get('https://sdk-api-v1.singular.net/api/v2/conversion_value', params=params)
print(response.json())

 

リクエストレスポンス

以下は、新しい変換値が返され、APIレスポンスが成功した例である。

HTTPレスポンス
200 - ok

レスポンス・ボディにエラーや理由のない200 - okは、リクエストが処理のためにキューに送られたことを意味する。

 

レスポンス
{
   "conversion_value":1,
   "skan_updated_coarse_value":0,
   "postback_sequence_index":0,
   "status":"ok"
}

レスポンス・パラメータ

以下の表は、レスポンス・パラメータを定義したものである。

キー 説明 例 値
conversion_value

新しい細かい変換値

0-63
skan_updated_coarse_value

新しい粗変換値

0-2
postback_sequence_index

SKANのポストバック測定期間に対応:

  • 0 = ポストバック1
  • 1 = ポストバック2
  • 2 = ポストバック3

どの粗変換値キーが更新されるべきかを示す:すなわちp0_coarse, p1_coarse, p2_coarse

0-2
status

正常に処理された場合は Ok

ok

考えられるレスポンス・エラー

  • 最後の変換値更新(28032時間)から24時間以上経過しているため、更新ウィンドウは閉じられています:
    • として計算された時間:
      (skan_last_call_to_skadnetwork_timestamp) - (skan_first_call_to_skadnetwork_timestamp)
  • 不明なプラットフォームエラー - iOS以外のプラットフォーム
  • 変換管理:無効なパラメータ %x が指定されました。
  • コンバージョン管理:アプリの変換モデルが見つかりません:%x
  • 無効な測定期間: %x
  • コンバージョン管理:所有者の通貨が見つかりません: %customer

オプション・パラメータ

次の表は、SKAdNetwork バージョン 4 をサポートするために使用されるオプション・パラメータの一覧です。記載されているパラメータはすべてクエリ・パラメータです。

オプション・パラメータ
変換値パラメータ
パラメータ 説明
p0_coarse

サポートされているプラットフォーム

  • iOS 16.1+
int

必須ではありません。このキーはskan_current_conversion_valueキーからマッピングされる。言い換えると、モデルはレスポンスに返すskan_updated_coarse_valueを評価する際にp0_coarseを使用せず、skan_current_conversion_valueを使用します。これは(0-2)の間の整数である。

 

値の例

0
パラメータ 説明
p0_prev_coarse_value

サポートされているプラットフォーム

  • iOS 16.1+
int

p0の前の粗い値。これは(0-2)の間の整数です。

 

値の例

0
パラメータ 説明
p1_prev_coarse_value

サポートされているプラットフォーム

  • iOS 16.1+
int

p1の前の粗い値。これは(0-2)の間の整数です。

 

値の例:

0
p2_prev_coarse_value

対応プラットフォーム

  • iOS 16.1+
int

p2の前の粗い値。(0-2)の整数値。

 

値の例

0
収益トラッキング
p0_total_iap_revenue

サポートされているプラットフォーム

  • iOS 16.1+
JSON URL-encoded string

広告収益以外のp0のIAP収益の合計。

{
   "USD":9.99
}

 

%7B%22USD%22%3A9.99%7D
p1_total_iap_revenue

対応プラットフォーム

  • iOS 16.1+
JSON URL-encoded string

広告収益以外のp1のIAP収益の合計。

{
   "USD":9.99
}

 

%7B%22USD%22%3A9.99%7D
p2_total_iap_revenue

対応プラットフォーム:

  • iOS 16.1+
JSON URL-encoded string

広告収益以外のp2のIAP収益の合計。

{
   "USD":9.99
}

 

%7B%22USD%22%3A9.99%7D
p0_total_admon_revenue

対応プラットフォーム:

  • iOS 16.1+
JSON URL-encoded string

p0の広告収益合計。

{
   "USD":1.2
}

 

例値:

%7B%22USD%22%3A1.2%7D
p1_total_admon_revenue

対応プラットフォーム

  • iOS 16.1+
JSON URL-encoded string

p1の広告収益合計。

{
   "USD":1.2
}

 

例値:

%7B%22USD%22%3A1.2%7D
p2_total_admon_revenue

対応プラットフォーム

  • iOS 16.1+
JSON URL-encoded string

p2の広告収益合計。

{
   "USD":1.2
}

 

値の例

%7B%22USD%22%3A1.2%7D
タイムスタンプパラメータ
パラメータ 説明
p0_window_lock

サポートされているプラットフォーム

  • iOS 16.1+
int

p0のウィンドウロックを伴う最終更新のUnixタイムスタンプ。

注 - 単数変換モデルでは、現在のところウィンドウロックは考慮されていませんが、いずれは考慮されるかもしれません。

 

値の例

1483228850
パラメータ 説明
p1_window_lock

サポートされているプラットフォーム

  • iOS 16.1+
int

p1のウィンドウロックによる最終更新のUnixタイムスタンプ。

注 - 単数変換モデルでは、現在のところウィンドウロックは考慮されていませんが、いずれは考慮されるかもしれません。

 

値の例

1483228850
パラメータ 説明
p2_window_lock

サポートされているプラットフォーム

  • iOS 16.1+
int

p2のウィンドウロックを伴う最終更新のUnixタイムスタンプ。

注 - 単数変換モデルでは、現在のところウィンドウロックは考慮されていませんが、いずれ考慮される可能性があります。

 

値の例

1483228850