切り分けの順序はこうです。まず広告自体が出ているか(impression があるか)を Ad Inspector で確認します。次に、出ているのに最適化が回らないなら、そのネットワークのインストールが計測されているかを疑います。SKAdNetwork は、広告ネットワークの ID があらかじめアプリの Info.plist に登録されていて初めてポストバック(インストールの通知)が成立します。登録がなければ、そのネットワーク経由のインストールは「誰の成果でもない」扱いになり、入札最適化の燃料が供給されません。
ここに登録された ID のネットワークだけが、StoreKit を通じてインストールのポストバックを受け取れます。メディエーションでパートナーを足すという行為は、実質的に「新しい広告ネットワークを呼び出す」ことなので、そのネットワークの ID が Info.plist になければ、配信はできても計測ができない、という片肺状態が生まれます。
私の4アプリは広告構成がほぼ共通なので、本来は同じ ID セットが入っているべきでした。ところが実際には、あるアプリには Liftoff の ID があるのに別のアプリにはない、といった小さなズレが溜まっていました。手で4回 Info.plist を編集すれば、1回くらいは抜けます。それが今回の「ムラのある劣化」の正体でした。
突き合わせ自体は短いスクリプトで足ります。Info.plist から現在登録されている ID を抜き出し、正本が要求する ID と比較して、不足と余剰を出します。
#!/usr/bin/env python3# reconcile_skadnetwork.py — Info.plist の SKAdNetworkItems を正本と突き合わせるimport plistlib, sys, yaml, pathlibCANON = yaml.safe_load(open("skadnetwork_canonical.yaml"))NET_IDS = CANON["networks"]def registered_ids(plist_path: str) -> set[str]: with open(plist_path, "rb") as f: data = plistlib.load(f) items = data.get("SKAdNetworkItems", []) return {i["SKAdNetworkIdentifier"].lower() for i in items if "SKAdNetworkIdentifier" in i}def required_ids(app_key: str) -> set[str]: ids = set() for net in CANON["apps"][app_key]: ids.update(s.lower() for s in NET_IDS[net]) return idsdef main(mapping: dict[str, str]) -> int: exit_code = 0 for app_key, plist in mapping.items(): have = registered_ids(plist) need = required_ids(app_key) missing = sorted(need - have) # 計測が欠落しているネットワーク extra = sorted(have - need) # 正本にない=出所不明な ID status = "OK" if not missing and not extra else "DRIFT" print(f"[{status}] {app_key}: missing={missing} extra={extra}") if missing or extra: exit_code = 1 return exit_codeif __name__ == "__main__": # app_key -> Info.plist のパス MAPPING = { "beautiful_wallpapers": "apps/BeautifulWallpapers/Info.plist", "ukiyoe_wallpapers": "apps/UkiyoeWallpapers/Info.plist", "relaxing_healing": "apps/RelaxingHealing/Info.plist", "law_of_attraction": "apps/LawOfAttraction/Info.plist", } sys.exit(main(MAPPING))
missing は計測が欠落しているネットワーク、extra は正本に載っていない出所不明の ID です。後者をそのままにすると、過去に手で足した古い ID や、タイプミスした ID が残り続けます。私の環境では初回実行で、4アプリ中3アプリに missing、1アプリに重複由来の extra が出ました。手作業の積み重ねがそのまま差分として可視化された瞬間でした。
このスクリプトは終了コードで合否を返すので、後述のビルド前ゲートにそのまま組み込めます。extra を警告どまりにするか不合格にするかは、運用の厳しさで選べます。私は「出所不明 ID は残さない」方針なので不合格にしています。
エージェントには「直す」前に「差分を証拠つきで報告」させる
照合の結果を受けて Info.plist を実際に整えるところで、Antigravity のエージェントを使いました。ただし、いきなり編集させると、気を利かせて正本にない ID まで足してくることがあります。そこで指示を二段階に分けました。
# エージェントへの指示(要旨)前提:- 正本は skadnetwork_canonical.yaml のみ。ここに無い SKAdNetworkID は 絶対に追加・提案しないこと。新しい ID をどこかから「補完」してはならない。タスク(ステップ1・報告のみ):- reconcile_skadnetwork.py を実行し、各アプリの missing / extra を表で示す。- 各 missing がどのネットワークに対応するかを正本の行と対応づけて示す。- この段階ではファイルを編集しない。タスク(ステップ2・私の承認後):- 承認した missing のみ、対応する Info.plist の SKAdNetworkItems に追記する。- extra(出所不明 ID)は削除候補として列挙し、削除可否は私に確認する。- 変更後に reconcile_skadnetwork.py を再実行し、全アプリ OK になった出力を貼る。完了条件:- 「直しました」ではなく、再実行後の OK 出力を証拠として示すこと。