Показ пейвола с таргетингом Apple Ads при первом запуске во Flutter SDK
Атрибуция Apple Ads (AA) поступает асинхронно после вызова Adapty().activate(). При первом запуске она, как правило, ещё не получена, поэтому если сразу вызвать getPaywall, Adapty обработает запрос по аудитории по умолчанию, и пользователи Apple Ads не увидят пейвол, настроенный для AA-сегмента. Вместо того чтобы показывать один пейвол, а затем заменять его другим, подождите немного, пока не придёт атрибуция AA: если она поступит в течение короткого таймаута — покажите целевой пейвол, если нет — пейвол аудитории по умолчанию. AdaptyProfile.appliedAttributionSources сообщает, когда атрибуция AA была применена.
Прежде чем начать
Вам понадобится:
- Adapty Flutter SDK 3.17.0 или новее.
- Apple Ads, настроенный для приложения в Adapty. См. Apple Ads.
Как это работает
После Adapty().activate() SDK в фоне запрашивает у Apple данные атрибуции Apple Ads и передаёт результат на сервер Adapty. Когда AA становится активным источником атрибуции для профиля, SDK доставляет обновлённый AdaptyProfile в слушатель didUpdateProfileStream, а в списке appliedAttributionSources появляется AdaptyAttributionSource.appleAds.
При первом запуске возможны два исхода:
- Атрибуция поступает в рамках таймаута. Вызовите
getPaywall— Adapty обработает запрос с учётом аудитории Apple Ads и вернёт целевой пейвол. - Таймаут истекает первым. В этом случае покажите пейвол для аудитории по умолчанию — пользователи без атрибуции Apple Ads не будут ждать.
getPaywallForDefaultAudienceвернёт его без ожидания сегментации.
appliedAttributionSources может быть пустым. Это означает одно из двух:
- атрибуция Apple Ads ещё не обработана для этого профиля, или
- атрибуция не поступила вовсе.
В любом случае вызов
getPaywallForDefaultAudienceбезопасен — он возвращает пейвол для аудитории по умолчанию вне зависимости от состояния профиля.
Ожидание актуально только при первом запуске. После того как атрибуция Apple Ads записана, она постоянно хранится в профиле. При каждом последующем запуске кешированный профиль уже содержит AdaptyAttributionSource.appleAds в appliedAttributionSources, поэтому путь атрибуции разрешается немедленно и getPaywall возвращает пейвол для сегмента Apple Ads без какой-либо задержки.
Реализация
При первом запуске дождитесь AdaptyAttributionSource.appleAds и установите жёсткий таймаут — если атрибуция Apple Ads так и не пришла, эти пользователи всё равно должны увидеть пейвол.
- Активируйте SDK. См. Установка и настройка Flutter SDK.
- Подпишитесь на обновления профиля с помощью
Adapty().didUpdateProfileStream.listen(…). Если вы ещё не настроили слушатель, см. Отслеживание обновлений подписки. - Отслеживайте
AdaptyAttributionSource.appleAdsвappliedAttributionSources. Когда он появится, загрузите пейвол с помощью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 — это широковещательный поток (broadcast stream), поэтому он поддерживает несколько независимых слушателей, не мешая друг другу.