WWDC Quick Look 💓 By SwiftGGTeam
What’s new in AdAttributionKit

What’s new in AdAttributionKit

元の動画を見る

ハイライト

iOS 18.4 から、同一の App が複数の有効な re-engagement コンバージョンウィンドウを同時に保持できるようになり、conversion tag によってそれぞれの広告リエンゲージメントを正確にトラッキングできるようになりました。


主要内容

広告アトリビューションには長らく避けられてきた問題があります。それは「同時に走らせられる re-engagement コンバージョンが 1 つだけ」という制約です。たとえば 2 つのプロモーション広告を同時配信していて、ユーザーがまず Discount 1 をタップして App を開いたものの注文せず、数時間後に Discount 2 をタップし、最終的に Discount 1 の商品を購入したケースを考えてみます。iOS 18.3 以前では先のコンバージョンが既にロックされており、購入データは Discount 2 にしか紐付きませんでした。最初の広告が本来持っていた価値は埋もれてしまい、運用担当者は ROI データと肌感覚が合わない理由を、アトリビューション側では補正できないまま抱え込むことになります。

iOS 18.4 ではこの制約が取り払われました。複数の re-engagement コンバージョンウィンドウが同時に存在でき、それぞれに conversion tag が付与されます。これはコンバージョンに対する「ブックマーク」のようなものです。App 側で Info.plistEligibleForAdAttributionKitOverlappingConversionsYES に設定すれば、AdAttributionKit は re-engagement URL に query parameter を付加します。App はその tag を抽出してローカルやサーバーに永続化しておき、ユーザーが実際に購入したタイミングでその tag を使って対応する postback を更新します。アトリビューションの考え方は「購入に最も近い広告は誰か」から、「ユーザーは結局どの広告によって呼び戻されたのか」へと変わります。


詳細

仕組み全体は Postback.reengagementOpenURLParameter という 1 つの query item を中心に動きます。App が Universal Link を受け取ったあと、URL から tag の値を取り出します(05:42)。

func retrieveConversionTag(fromURL url: URL) -> String? {
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
        print("Could not get components for URL.")
        return nil
    }

    guard let queryItems = components.queryItems else {
        print("URL does not contain query items.")
        return nil
    }

    for item in queryItems {
        guard item.name == Postback.reengagementOpenURLParameter else {
            continue
        }
        return item.value
    }
    return nil
}

ポイントは次のとおりです。

  • URLComponents(url:resolvingAgainstBaseURL:) で deep link をパースします。失敗時はそのまま nil を返します。
  • queryItems で URL の全 query parameter を取得します。存在しなければリエンゲージメント由来のリンクではないと判断します。
  • Postback.reengagementOpenURLParameter と比較します。AdAttributionKit は re-engagement で開かれる際に必ずこのパラメータを付与します。
  • 見つかったらすぐ value を返却します。これが後段の更新で使う conversion tag です。

tag を取得したあとは、ユーザーが有効なアクションを行ったタイミングで業務側から更新 API を呼び出します(06:55)。

func updateConversionValue(_ conversionValue: Int, conversionTag: String) async {
    do {
        let update = PostbackUpdate(fineConversionValue: conversionValue,
                                    lockPostback: false,
                                    conversionTag: conversionTag)
        try await Postback.updateConversionValue(update)
    }
    catch {
        print("An error occurred while updating the conversion value: \(error)")
    }
}

ポイントは次のとおりです。

  • PostbackUpdatefineConversionValuelockPostbackconversionTag の 3 つのフィールドを同時に受け取ります。
  • lockPostback: false はその後の更新も許可することを意味し、複数ステップのファネルを扱うシナリオに向いています。
  • conversionTag がピンポイント更新の鍵です。指定しない場合、システムは直近のコンバージョンしか更新しません。
  • Postback.updateConversionValue は async throws なので、呼び出し側は並行処理コンテキストでエラーをハンドリングする必要があります。

2 つめの大きな話題は attribution window と cooldown の設定可能化です。Info.plistAdAttributionKitConfigurations フィールドが追加され、ad network 単位でウィンドウ長を宣言できます(10:14)。

{
  "AdAttributionKitConfigurations": {
    "AttributionWindows": {
      "com.example.adNetwork": {
        "install": {
          "click": 2,
          "ignoreInteractionType": "view"                     
        }
      }
    }
 }

ポイントは次のとおりです。

  • AttributionWindows の配下では ad network identifier を key にして、ネットワークごとに個別の設定が可能です。
  • install の子ノードは install 系のアトリビューションを記述します。re-engagement にはウィンドウ設定がありません。コンバージョンがインタラクションのすぐ後に発生するためです。
  • click はクリック型広告のアトリビューションウィンドウ日数を指定します。デフォルトは 30 日で、短くも長くもできます。
  • ignoreInteractionType"view" または "click" のいずれかを設定し、特定の種類のインタラクションをそのネットワークのアトリビューション対象から完全に除外します。
  • 一括で設定したい場合は global key を追加してフォールバックにできます。特定 ad network の設定はグローバル値を上書きします(10:46)。

cooldown も同じ設定入口で「コンバージョン後のロック期間」を表現します(13:52)。install-cooldown-hours を 6、reengagement-cooldown-hours を 1 に設定すれば、install コンバージョン発生から数分後に re-engagement にアトリビューションを横取りされる事態を避けられます。

postback には今回 country-code も追加されました(17:05)。値は ISO 3166 に準拠した 2 文字の国コードで、たとえば "MT" のように表されます。Marketplace のシナリオでは、install verification token の payload にも対応する ccode フィールドが追加され(16:26)、広告ネットワークが地域ごとに予算効果を切り分けられるようになりました。


重要ポイント

  • overlapping conversions を有効化し、conversion tag を組み込む:取り組む価値:複数の re-engagement を同時並行で走らせるのはセール期間の常識であり、旧来のルールでは早期に当たった広告の真の価値が体系的に過小評価されてしまいます。始め方:Info.plistEligibleForAdAttributionKitOverlappingConversions=YES を追加し、Universal Link のハンドラ関数で Postback.reengagementOpenURLParameter を読み取って、その tag をローカルの注文コンテキストと紐付けます。

  • ad network ごとにアトリビューションウィンドウを個別に調整する:取り組む価値:デフォルト 30 日の click ウィンドウは短期セールには合わず、無関係なトラフィックまでアトリビューションに巻き込みます。ウィンドウを短縮することで、データが本当の購入動機を反映するようになります。始め方:まず App Analytics で各ネットワークの実際のコンバージョン到達時間の分布を確認し、コンバージョン時間が短いネットワークには AttributionWindows で 2〜7 日の click ウィンドウを設定します。純粋なディスプレイ型ネットワークには ignoreInteractionType: "view" で除外を明示します。

  • 重要なコンバージョン経路に cooldown を設定する:取り組む価値:install 完了から数分のうちに re-engagement にかすめ取られるアトリビューション誤差は実在し、新規獲得チームと再獲得チームのデータ同士がぶつかる原因になります。始め方:IAP と install の時間間隔分布をまず観察し、install-cooldown-hours を多くのユーザーが初回購入を終える時間(たとえば 6 時間)に、reengagement-cooldown-hours を 1 時間に設定します。そのうえで iOS の Settings にある AdAttributionKit のテスト入口を使ってエンドツーエンドで疎通させます。

  • postback の取り込みに country-code の次元を組み込む:取り組む価値:同じ ROAS でも国によって意味するものは大きく異なり、地域単位で切り分けて初めて本当に儲かる市場を特定できます。始め方:postback の取り込みスクリプトに country-code フィールドを追加します。Marketplace のフローでは token payload の ccode も別途パースし、コンプライアンスと突合の両方に使える証跡として活用します。


関連 Session

コメント

GitHub Issues · utterances