Mostrar un paywall dirigido por AA en el primer lanzamiento con el SDK de Flutter

Este artículo aplica a la compilación iOS de tu app. La atribución de Apple Ads solo existe en iOS.

Apple Ads (AA) attribution llega de forma asíncrona después de Adapty().activate(). En el primer arranque, normalmente aún no ha llegado, así que si llamas a getFlow de inmediato, Adapty resuelve la solicitud con la audiencia predeterminada y los usuarios de Apple Ads no ven el paywall segmentado por AA. En lugar de mostrar un paywall y luego reemplazarlo, espera brevemente a que llegue la atribución de AA antes de mostrar nada: muestra el paywall segmentado si la atribución llega dentro de un breve tiempo de espera, o el paywall de la audiencia predeterminada si no llega. AdaptyProfile.appliedExternalAttributionProviders indica cuándo se ha aplicado la atribución de AA.

Important

Esta propiedad solo reporta Apple Ads. La atribución de otros proveedores es visible en los perfiles de usuario y está disponible en los filtros de segmento, pero aún no aquí.

Antes de empezar

Necesitas:

  • Adapty Flutter SDK 4.1 o posterior. En la versión 4.0.x, la propiedad del perfil se llama appliedAttributionSources y sus valores son AdaptyAttributionSource — consulta Migrar a v4.1. En las versiones 3.17.0–3.x, los flows también se obtienen con getPaywall/getPaywallForDefaultAudience y el tipo devuelto es AdaptyPaywall — consulta Migrar a v4.0.
  • Apple Ads configurado para la aplicación en Adapty. Consulta Apple Ads.

Cómo funciona

Tras llamar a Adapty().activate(), el SDK solicita en segundo plano la atribución de Apple Ads a Apple y reenvía el resultado al backend de Adapty. Cuando AA se convierte en la fuente de atribución activa para el perfil, el SDK entrega un AdaptyProfile actualizado a tu listener didUpdateProfileStream, con AdaptyExternalAttributionProvider.appleAds en su lista appliedExternalAttributionProviders.

En el primer lanzamiento, esto genera dos resultados que debes gestionar:

  1. La atribución llega antes de que expire el tiempo de espera. Llama a getFlow — Adapty resuelve la solicitud con la audiencia de Apple Ads y devuelve el paywall objetivo.
  2. El tiempo de espera expira primero. Muestra el paywall de la audiencia predeterminada en su lugar, para que los usuarios sin atribución de Apple Ads no tengan que esperar. getFlowForDefaultAudience lo devuelve sin esperar a la segmentación.

appliedExternalAttributionProviders puede estar vacío. Eso significa una de las siguientes situaciones:

  • La atribución de Apple Ads aún no se ha procesado para este perfil.
  • No ha llegado ninguna atribución.
  • La atribución llegó desde otro proveedor, que este array no informa.

En los tres casos, getFlowForDefaultAudience es seguro de llamar: devuelve el paywall de audiencia predeterminada independientemente del estado del perfil.

Important

La espera solo se aplica al primer lanzamiento. Una vez que se ha registrado la atribución de Apple Ads, queda almacenada en el perfil de forma permanente. En cada lanzamiento posterior, el perfil en caché ya incluye AdaptyExternalAttributionProvider.appleAds en appliedExternalAttributionProviders, por lo que la ruta de atribución se resuelve de inmediato y getFlow devuelve el paywall segmentado por Apple Ads sin ningún retraso.

Implementación

En el primer lanzamiento, espera a AdaptyExternalAttributionProvider.appleAds y aplica un tiempo límite estricto: si la atribución de Apple Ads nunca llega, esos usuarios igualmente deben ver un paywall.

  1. Activa el SDK. Consulta Instalar y configurar el SDK de Flutter.
  2. Suscríbete a las actualizaciones de perfil con Adapty().didUpdateProfileStream.listen(…). Si aún no has configurado el listener, consulta Escuchar actualizaciones de suscripción.
  3. Observa AdaptyExternalAttributionProvider.appleAds en appliedExternalAttributionProviders. Cuando aparezca, carga el paywall con getFlow — Adapty devuelve la variante segmentada por 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 es un stream de difusión y no reproduce eventos anteriores, así que comprueba también el perfil actual una vez con getProfile(). En reinicios de la app, la atribución ya almacenada se aplica en el momento y no vuelve a emitirse.

  1. Inicia un temporizador de 3 a 5 segundos en paralelo con la suscripción. Si el temporizador se activa antes de que aparezca AdaptyExternalAttributionProvider.appleAds, carga el paywall de la audiencia predeterminada con getFlowForDefaultAudience. Muestra el primer paywall que se resuelva y cancela el otro proceso, para que el paywall no se obtenga dos veces. Configura un paywall de respaldo para el placement, de modo que el usuario nunca se quede bloqueado si la solicitud de red falla.

Ejemplo completo

La implementación que se muestra a continuación compite en velocidad entre la atribución y un timeout, precarga el paywall de audiencia predeterminada en paralelo y devuelve el paywall que corresponda. El código que llama a esta función solo espera el resultado de una sola función — sin listeners ni indicadores de estado que gestionar:

  • Si la atribución llega antes del timeout, devuelve el paywall segmentado mediante getFlow.
  • Si el timeout vence primero, devuelve el paywall de audiencia predeterminada precargado mediante 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;
}

Llama esto desde tu splash screen y luego muestra el paywall cuando se resuelva:

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
}

Ajusta timeout según el tiempo que estés dispuesto a hacer esperar a los usuarios antes de que aparezca cualquier paywall. La mayoría de los usuarios no tienen atribución de Apple Ads, así que esperan el timeout completo: entre 3 y 5 segundos es un equilibrio razonable. La atribución, cuando llega, suele hacerlo en pocos segundos tras el lanzamiento.

Si tu app ya escucha didUpdateProfileStream para otros fines (por ejemplo, comprobar el estado de la suscripción), no necesitas modificarla. didUpdateProfileStream es un broadcast stream, por lo que admite múltiples listeners independientes sin que unos afecten a los otros.