在 Flutter SDK 中首次启动时显示 AA 定向付费墙

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 请求 Apple Ads 归因数据,并将结果转发至 Adapty 后端。当 AA 成为该用户画像的有效归因来源时,SDK 会向你的 didUpdateProfileStream 监听器推送更新后的 AdaptyProfile,其 appliedAttributionSources 列表中将包含 AdaptyAttributionSource.appleAds

首次启动时,你需要处理以下两种情况:

  1. 归因在超时时间内完成。 调用 getPaywall — Adapty 根据 Apple Ads 目标受众解析请求,并返回对应的付费墙。
  2. 超时时间先到。 改为显示默认目标受众的付费墙,避免没有 Apple Ads 归因的用户白白等待。getPaywallForDefaultAudience 无需等待分群即可直接返回结果。

appliedAttributionSources 可以为空,这意味着:

  • 该用户画像的 Apple Ads 归因尚未处理完成,或
  • 根本没有收到任何归因数据。 无论如何,getPaywallForDefaultAudience 都可以安全调用——它会直接返回默认受众的付费墙,不受用户画像状态影响。

等待仅发生在首次启动时。一旦 Apple Ads 归因数据记录完成,它就会永久保存在用户画像中。在此后的每次启动时,缓存的用户画像已经在 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);
  // present the segmented paywall, then cancel the subscription and the timer
});

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 是一个广播流,支持多个独立监听器互不干扰。