ÿÿ åˆå›žèµ·å‹•時ã«Apple Adså‘ã‘ペイウォールを表示ã™ã‚‹ | Flutter SDK | Adapty ドキュメント

FlutterSDKã§åˆå›žèµ·å‹•時ã«Apple Adså‘ã‘ペイウォールを表示ã™ã‚‹

Apple Ads (AA) ã®ã‚¢ãƒˆãƒªãƒ“ューション㯠Adapty().activate() ã®å¾Œã«éžåŒæœŸã§å±Šãã¾ã™ã€‚åˆå›žèµ·å‹•時ã¯ã¾ã å±Šã„ã¦ã„ãªã„ã“ã¨ãŒå¤šã„ãŸã‚ã€ã™ãã« getPaywall を呼ã³å‡ºã™ã¨ã€Adapty ã¯ãƒ‡ãƒ•ォルトオーディエンスã«åŸºã¥ã„ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’解決ã—ã¦ã—ã¾ã„ã€Apple Ads ユーザー㌠AA セグメントå‘ã‘ペイウォールを見逃ã—ã¦ã—ã¾ã„ã¾ã™ã€‚ペイウォールを表示ã—ã¦ã‹ã‚‰å·®ã—替ãˆã‚‹ã®ã§ã¯ãªãã€ä½•も表示ã™ã‚‹å‰ã« AA アトリビューションを少ã—ã®é–“å¾…ã¡ã¾ã—ょã†ã€‚アトリビューションãŒçŸ­ã„タイムアウト内ã«å±Šã‘ã°ã‚¿ãƒ¼ã‚²ãƒƒãƒˆå‘ã‘ペイウォールをã€å±Šã‹ãªã‘れã°ãƒ‡ãƒ•ォルトオーディエンスã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’表示ã—ã¾ã™ã€‚AdaptyProfile.appliedAttributionSources を使ã†ã¨ã€AA アトリビューションãŒé©ç”¨æ¸ˆã¿ã‹ã©ã†ã‹ã‚’確èªã§ãã¾ã™ã€‚

å§‹ã‚ã‚‹å‰ã«

å¿…è¦ãªã‚‚ã®ï¼š

  • Adapty Flutter SDK 3.17.0 以é™ã€‚
  • Adaptyã§ã‚¢ãƒ—リã®Apple AdsãŒè¨­å®šæ¸ˆã¿ã§ã‚ã‚‹ã“ã¨ã€‚詳ã—ãã¯Apple Adsã‚’å‚ç…§ã—ã¦ãã ã•ã„。

仕組ã¿

Adapty().activate() を呼ã³å‡ºã™ã¨ã€SDKã¯ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã§Apple Ads(AA)ã®ã‚¢ãƒˆãƒªãƒ“ューション情報をAppleã«å•ã„åˆã‚ã›ã€ãã®çµæžœã‚’Adaptyã®ãƒãƒƒã‚¯ã‚¨ãƒ³ãƒ‰ã«è»¢é€ã—ã¾ã™ã€‚AAãŒãã®ãƒ—ロファイルã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªã‚¢ãƒˆãƒªãƒ“ューションソースã«ãªã‚‹ã¨ã€SDKã¯didUpdateProfileStreamãƒªã‚¹ãƒŠãƒ¼ã«æ›´æ–°æ¸ˆã¿ã®AdaptyProfileを届ã‘ã¾ã™ã€‚ã“ã®ã¨ãã€appliedAttributionSourcesリストã«ã¯AdaptyAttributionSource.appleAdsãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚

åˆå›žèµ·å‹•時ã«ã¯ã€æ¬¡ã®2ã¤ã®ã‚±ãƒ¼ã‚¹ã‚’処ç†ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼š

  1. タイムアウト内ã«ã‚¢ãƒˆãƒªãƒ“ューションãŒå±Šã„ãŸå ´åˆã€‚ getPaywall を呼ã³å‡ºã™ã¨ã€Adapty ㌠Apple Ads ã®ã‚ªãƒ¼ãƒ‡ã‚£ã‚¨ãƒ³ã‚¹ã«å¯¾ã—ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’解決ã—ã€ã‚¿ãƒ¼ã‚²ãƒƒãƒˆã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’è¿”ã—ã¾ã™ã€‚
  2. å…ˆã«ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆãŒçµŒéŽã—ãŸå ´åˆã€‚ 代ã‚りã«ãƒ‡ãƒ•ォルトオーディエンスã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’表示ã—ã€Apple Ads ã®ã‚¢ãƒˆãƒªãƒ“ューションãŒãªã„ユーザーを待ãŸã›ãªã„よã†ã«ã—ã¾ã—ょã†ã€‚getPaywallForDefaultAudience ã¯ã‚»ã‚°ãƒ¡ãƒ³ãƒˆå‡¦ç†ã‚’å¾…ãŸãšã«å³åº§ã«è¿”ã—ã¾ã™ã€‚

appliedAttributionSources ã¯ç©ºã«ãªã‚‹ã“ã¨ãŒã‚りã¾ã™ã€‚ã“ã‚Œã¯æ¬¡ã®ã„ãšã‚Œã‹ã‚’æ„味ã—ã¾ã™ï¼š

  • ã“ã®ãƒ—ロファイルã«å¯¾ã™ã‚‹ Apple Ads ã®ã‚¢ãƒˆãƒªãƒ“ューションãŒã¾ã å‡¦ç†ã•れã¦ã„ãªã„ã€ã¾ãŸã¯
  • アトリビューション自体ãŒå±Šã„ã¦ã„ãªã„。 ã„ãšã‚Œã®å ´åˆã‚‚ã€getPaywallForDefaultAudience ã¯å®‰å…¨ã«å‘¼ã³å‡ºã›ã¾ã™ — プロファイルã®çŠ¶æ…‹ã«é–¢ã‚らãšã€ãƒ‡ãƒ•ォルトオーディエンスå‘ã‘ã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’è¿”ã—ã¾ã™ã€‚

ã“ã®å¾…機ãŒç™ºç”Ÿã™ã‚‹ã®ã¯åˆå›žèµ·å‹•時ã®ã¿ã§ã™ã€‚Apple Ads ã®ã‚¢ãƒˆãƒªãƒ“ューションãŒä¸€åº¦è¨˜éŒ²ã•れるã¨ã€ãƒ—ãƒ­ãƒ•ã‚¡ã‚¤ãƒ«ã«æ°¸ç¶šçš„ã«ä¿å­˜ã•れã¾ã™ã€‚2回目以é™ã®èµ·å‹•ã§ã¯ã€ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã•れãŸãƒ—ロファイルã«ã¯æ—¢ã« appliedAttributionSources ã« AdaptyAttributionSource.appleAds ãŒå«ã¾ã‚Œã¦ã„ã‚‹ãŸã‚ã€ã‚¢ãƒˆãƒªãƒ“ューションã®ãƒ‘スã¯ã™ãã«è§£æ±ºã•れã€getPaywall ã¯é…å»¶ãªã Apple Ads セグメントå‘ã‘ã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’è¿”ã—ã¾ã™ã€‚

実装

åˆå›žèµ·å‹•時㯠AdaptyAttributionSource.appleAds ã‚’å¾…ã¡ã€ãƒãƒ¼ãƒ‰ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚’設定ã—ã¦ãã ã•ã„。Apple Ads ã®ã‚¢ãƒˆãƒªãƒ“ューションãŒå±Šã‹ãªã‹ã£ãŸå ´åˆã§ã‚‚ã€è©²å½“ユーザーã«ã¯ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’表示ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚

  1. SDKを有効化ã™ã‚‹ã€‚ Flutter SDKã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã¨è¨­å®šã‚’å‚ç…§ã—ã¦ãã ã•ã„。
  2. Adapty().didUpdateProfileStream.listen(…)ã§ãƒ—ãƒ­ãƒ•ã‚¡ã‚¤ãƒ«ã®æ›´æ–°ã‚’購読ã™ã‚‹ã€‚ リスナーをã¾ã è¨­å®šã—ã¦ã„ãªã„å ´åˆã¯ã€ã‚µãƒ–ã‚¹ã‚¯ãƒªãƒ—ã‚·ãƒ§ãƒ³ã®æ›´æ–°ã‚’リッスンã™ã‚‹ã‚’å‚ç…§ã—ã¦ãã ã•ã„。
  3. appliedAttributionSourcesã«AdaptyAttributionSource.appleAdsãŒç¾ã‚Œã‚‹ã®ã‚’監視ã™ã‚‹ã€‚ ãれãŒç¾ã‚ŒãŸã‚‰ã€getPaywallã§ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’読ã¿è¾¼ã¿ã¾ã™ — Adaptyã¯AAセグメント済ã¿ã®ãƒãƒªã‚¢ãƒ³ãƒˆã‚’è¿”ã—ã¾ã™:
final subscription = Adapty().didUpdateProfileStream.listen((profile) async {
  if (!profile.appliedAttributionSources.contains(AdaptyAttributionSource.appleAds)) return;
  final paywall = await Adapty().getPaywall(placementId: placementId);
  // セグメント済ã¿ã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’表示ã—ã€ã‚µãƒ–スクリプションã¨ã‚¿ã‚¤ãƒžãƒ¼ã‚’キャンセルã™ã‚‹
});

didUpdateProfileStream ã¯ãƒ–ロードキャストストリームã§ã‚りã€éŽåŽ»ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’å†ç”Ÿã—ã¾ã›ã‚“。ãã®ãŸã‚ã€getProfile() を使ã£ã¦ç¾åœ¨ã®ãƒ—ロファイルも一度確èªã—ã¦ãã ã•ã„。アプリをå†èµ·å‹•ã—ãŸå ´åˆã€ä¿å­˜æ¸ˆã¿ã®ã‚¢ãƒˆãƒªãƒ“ューションã¯ã™ã§ã«é©ç”¨æ¸ˆã¿ã®ãŸã‚ã€å†åº¦ã‚¤ãƒ™ãƒ³ãƒˆã¯ç™ºè¡Œã•れã¾ã›ã‚“。 4. サブスクリプションã¨ä¸¦è¡Œã—ã¦3〜5ç§’ã®ã‚¿ã‚¤ãƒžãƒ¼ã‚’èµ·å‹•ã—ã¾ã™ã€‚ タイマーãŒAdaptyAttributionSource.appleAdsã®å—信より先ã«ç™ºç«ã—ãŸå ´åˆã¯ã€ä»£ã‚りã«getPaywallForDefaultAudienceを使用ã—ã¦ãƒ‡ãƒ•ォルトオーディエンスã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’読ã¿è¾¼ã¿ã¾ã™ã€‚最åˆã«è§£æ±ºã—ãŸæ–¹ã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’表示ã—ã€ã‚‚ã†ä¸€æ–¹ã®ãƒ‘スをキャンセルã—ã¦ã€ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ãŒäºŒé‡ã«å–å¾—ã•れãªã„よã†ã«ã—ã¾ã™ã€‚ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒå¤±æ•—ã—ã¦ã‚‚ユーザーãŒè¡Œãè©°ã¾ã‚‰ãªã„よã†ã€ãƒ—レースメントã«ãƒ•ォールãƒãƒƒã‚¯ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’設定ã—ã¦ãã ã•ã„。

完全ãªå®Ÿè£…例

以下ã®å®Ÿè£…ã§ã¯ã€ã‚¢ãƒˆãƒªãƒ“ューションã¨ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã‚’ç«¶åˆã•ã›ãªãŒã‚‰ã€ãƒ‡ãƒ•ォルトオーディエンスå‘ã‘ã®ãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’並行ã—ã¦ãƒ—リフェッãƒã—ã€é©åˆ‡ãªãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’è¿”ã—ã¾ã™ã€‚呼ã³å‡ºã—å…ƒã¯å˜ä¸€ã®é–¢æ•°ã‚’å¾…ã¤ã ã‘ã§ã‚ˆãã€ã‚³ãƒ¼ãƒ«ã‚µã‚¤ãƒˆã§ãƒªã‚¹ãƒŠãƒ¼ã‚„ステートフラグを管ç†ã™ã‚‹å¿…è¦ã¯ã‚りã¾ã›ã‚“。

  • アトリビューション㌠timeout 以内ã«å±Šã„ãŸå ´åˆã€getPaywall を通ã˜ã¦ã‚»ã‚°ãƒ¡ãƒ³ãƒˆåŒ–ã•れãŸãƒšã‚¤ã‚¦ã‚©ãƒ¼ãƒ«ã‚’è¿”ã—ã¾ã™ã€‚
  • timeout ãŒå…ˆã«çµŒéŽã—ãŸå ´åˆã€getPaywallForDefaultAudience を通ã˜ã¦ãƒ—ãƒªãƒ•ã‚§ãƒƒãƒæ¸ˆã¿ã®ãƒ‡ãƒ•ォルトオーディエンスå‘ã‘ペイウォールを返ã—ã¾ã™ã€‚

/// Returns the Apple Ads-segmented paywall if attribution is applied within
/// [timeout], otherwise the default-audience paywall. Call after Adapty().activate().
Future<AdaptyPaywall> getPaywallOrDefault({
  required String placementId,
  required Duration timeout,
}) {
  // Prefetch the default-audience paywall right away so the timeout path resolves
  // without an extra network round-trip. `getPaywallForDefaultAudience` 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().getPaywallForDefaultAudience(placementId: placementId)..ignore();

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

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

  void onProfile(AdaptyProfile profile) {
    if (profile.appliedAttributionSources.contains(AdaptyAttributionSource.appleAds)) {
      resolve(Adapty().getPaywall(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 getPaywallOrDefault(
    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 ã¯ãƒ–ロードキャストストリームãªã®ã§ã€è¤‡æ•°ã®ç‹¬ç«‹ã—ãŸãƒªã‚¹ãƒŠãƒ¼ãŒäº’ã„ã«å½±éŸ¿ã‚’与ãˆã‚‹ã“ã¨ãªã使用ã§ãã¾ã™ã€‚

ÿÿÿÿ