Flutter SDKで初回起動時にAA対象ペイウォールを表示する

この記事はアプリのiOSビルドに適用されます。Apple AdsのアトリビューションはiOSのみで利用可能です。

Apple Ads(AA)のアトリビューションは、Adapty().activate() の後に非同期で届きます。初回起動時はまだ届いていないことが多いため、すぐに getFlow を呼び出すと、Adapty はデフォルトのオーディエンスに基づいてリクエストを解決し、AA でセグメントされたペイウォールが Apple Ads ユーザーに表示されません。ペイウォールを一度表示してから差し替えるのではなく、何も表示する前に AA アトリビューションを短時間だけ待ちましょう。アトリビューションが短いタイムアウト内に届いた場合はターゲット向けのペイウォールを、届かなかった場合はデフォルトオーディエンスのペイウォールを表示します。AdaptyProfile.appliedExternalAttributionProviders を使うと、AA アトリビューションが適用されたかどうかを確認できます。

Important

このプロパティはApple Adsのみを報告します。他のプロバイダーからのアトリビューションはユーザープロファイルで確認でき、セグメントフィルターでも利用できますが、ここにはまだ表示されません。

始める前に

必要なもの:

  • Adapty Flutter SDK 4.1 以降。4.0.x では、プロファイルのプロパティ名は appliedAttributionSources で、値は AdaptyAttributionSource です。詳細は v4.1 への移行 を参照してください。3.17.0〜3.x では、フローも getPaywall/getPaywallForDefaultAudience で取得でき、返り値の型は AdaptyPaywall です。詳細は v4.0 への移行 を参照してください。
  • Apple Ads が Adapty のアプリ設定で構成されていること。詳細は Apple Ads を参照してください。

仕組み

Adapty().activate() の呼び出し後、SDKはバックグラウンドでApple AdsのアトリビューションをAppleにリクエストし、その結果をAdaptyのバックエンドに転送します。AAがプロファイルのアクティブなアトリビューションソースになると、SDKはdidUpdateProfileStreamリスナーに更新されたAdaptyProfileを届けます。このとき、appliedExternalAttributionProvidersリストにはAdaptyExternalAttributionProvider.appleAdsが含まれています。

初回起動時には、次の2つのケースに対応する必要があります。

  1. アトリビューションがタイムアウト内に届いた場合。 getFlow を呼び出すと、Adapty はリクエストを Apple Ads オーディエンスに対して解決し、ターゲティングされたペイウォールを返します。
  2. 先にタイムアウトが経過した場合。 代わりにデフォルトオーディエンスのペイウォールを表示します。これにより、Apple Ads のアトリビューションがないユーザーを待たせずに済みます。getFlowForDefaultAudience はセグメンテーションを待たずにそれを返します。

appliedExternalAttributionProviders は空になる場合があります。その場合、以下のいずれかを意味します。

  • このプロファイルの Apple Ads アトリビューションがまだ処理されていない。
  • アトリビューションがまったく届いていない。
  • 別のプロバイダーからアトリビューションが届いているが、この配列にはそのデータが含まれていない。

いずれの場合も、getFlowForDefaultAudience は安全に呼び出せます。プロファイルの状態にかかわらず、デフォルトオーディエンスのペイウォールを返します。

Important

この待機は初回起動時のみ適用されます。Apple Ads のアトリビューションが一度記録されると、それはプロファイルに永続的に保存されます。2回目以降の起動では、キャッシュされたプロファイルの appliedExternalAttributionProviders にすでに AdaptyExternalAttributionProvider.appleAds が含まれているため、アトリビューションパスはすぐに解決され、getFlow は遅延なく Apple Ads セグメント向けのペイウォールを返します。

実装

初回起動時は AdaptyExternalAttributionProvider.appleAds を待機し、ハードタイムアウトを設定してください。Apple Ads のアトリビューションが届かなかった場合でも、そのユーザーにはペイウォールを表示する必要があります。

  1. SDKを有効化する。 Flutter SDKのインストールと設定を参照してください。
  2. Adapty().didUpdateProfileStream.listen(…)でプロファイルの更新を購読する。 リスナーをまだ設定していない場合は、サブスクリプション更新のリッスンを参照してください。
  3. appliedExternalAttributionProviders内のAdaptyExternalAttributionProvider.appleAdsを監視する。 それが現れたら、getFlowでペイウォールを読み込みます — AdaptyはAAセグメント済みのバリアントを返します:
final subscription = Adapty().didUpdateProfileStream.listen((profile) async {
  if (!profile.appliedExternalAttributionProviders.contains(AdaptyExternalAttributionProvider.appleAds)) return;
  final paywall = await Adapty().getFlow(placementId: placementId);
  // present the segmented paywall, then cancel the subscription and the timer
});

didUpdateProfileStream はブロードキャストストリームであり、過去のイベントを再生しません。そのため、getProfile() を使って現在のプロファイルも一度確認してください。アプリを再起動した場合、保存済みのアトリビューションはすでに適用済みのため、再度イベントは発行されません。

  1. AdaptyExternalAttributionProvider.appleAds が返ってくる前にタイマーが発火した場合は、代わりに getFlowForDefaultAudience でデフォルトオーディエンスのペイウォールを読み込むよう、サブスクリプションと並行して3〜5秒のタイマーを開始します。 先に解決した方のペイウォールを表示してもう一方のパスはキャンセルし、ペイウォールが2回取得されないようにしてください。ネットワークリクエストが失敗してもユーザーが止まらないよう、プレースメントにフォールバックペイウォールを設定してください。

完全な実装例

以下の実装では、アトリビューションとタイムアウトを競わせながら、デフォルトオーディエンスのペイウォールを並行してプリフェッチし、適切なペイウォールを返します。呼び出し元は単一の関数を await するだけで済み、コールサイトでリスナーやステートフラグを管理する必要はありません。

  • アトリビューションが timeout 以内に取得できた場合、getFlow を通じてセグメント化されたペイウォールを返します。
  • timeout が先に経過した場合、getFlowForDefaultAudience を通じてプリフェッチされたデフォルトオーディエンスのペイウォールを返します。

/// Returns the Apple Ads-segmented paywall if attribution is applied within
/// [timeout], otherwise the default-audience paywall. Call after Adapty().activate().
Future<AdaptyFlow> getFlowOrDefault({
  required String placementId,
  required Duration timeout,
}) {
  // Prefetch the default-audience paywall right away so the timeout path resolves
  // without an extra network round-trip. `getFlowForDefaultAudience` skips the
  // wait for segmentation data. `..ignore()` keeps an unused prefetch from surfacing
  // as an unhandled error; the error still reaches the caller if this paywall wins.
  final defaultPaywall =
      Adapty().getFlowForDefaultAudience(placementId: placementId)..ignore();

  final completer = Completer<AdaptyFlow>();
  late final StreamSubscription<AdaptyProfile> subscription;
  late final Timer timer;

  void resolve(Future<AdaptyFlow> paywall) {
    if (completer.isCompleted) return;
    timer.cancel();
    subscription.cancel();
    completer.complete(paywall);
  }

  void onProfile(AdaptyProfile profile) {
    if (profile.appliedExternalAttributionProviders.contains(AdaptyExternalAttributionProvider.appleAds)) {
      resolve(Adapty().getFlow(placementId: placementId));
    }
  }

  // Attribution path: react to profile updates as attribution is applied.
  subscription = Adapty().didUpdateProfileStream.listen(onProfile);

  // The stream is a broadcast stream and doesn't replay, so check the current
  // profile too — on relaunches attribution is already stored and won't re-emit.
  Adapty().getProfile().then(onProfile).ignore();

  // Timeout path: fall back to the prefetched default-audience paywall.
  timer = Timer(timeout, () => resolve(defaultPaywall));

  return completer.future;
}

スプラッシュ画面から呼び出し、解決後にペイウォールを表示します:

try {
  final paywall = await getFlowOrDefault(
    placementId: 'YOUR_PLACEMENT_ID',
    timeout: const Duration(seconds: 5),
  );
  // present the paywall
} on AdaptyError catch (adaptyError) {
  // handle the error or show a fallback paywall
} catch (e) {
  // handle the error
}

timeout を調整して、ペイウォールが表示されるまでユーザーを待たせる時間を設定してください。ほとんどのユーザーはApple Adsのアトリビューションを持っていないため、設定したタイムアウトの全時間を待つことになります。3〜5秒が現実的なバランスです。アトリビューションが来る場合は、通常、アプリ起動から数秒以内に届きます。

アプリがすでに別の目的(例:サブスクリプションステータスの確認)で didUpdateProfileStream をリッスンしている場合、変更する必要はありません。didUpdateProfileStream はブロードキャストストリームなので、複数の独立したリスナーが互いに影響を与えることなく使用できます。