Hiển thị paywall theo đối tượng Apple Ads ngay lần đầu khởi chạy trong Flutter SDK
Bài viết này áp dụng cho bản build iOS của ứng dụng. Attribution từ Apple Ads chỉ có trên iOS.
Attribution từ Apple Ads (AA) được nhận bất đồng bộ sau khi gọi Adapty().activate(). Ở lần khởi chạy đầu tiên, dữ liệu này thường chưa về kịp, nên nếu bạn gọi getFlow ngay lập tức, Adapty sẽ xử lý yêu cầu theo đối tượng mặc định và người dùng Apple Ads sẽ bỏ lỡ paywall được phân khúc theo AA của bạn. Thay vì hiển thị một paywall rồi thay thế nó, hãy chờ một chút để nhận attribution từ AA trước khi hiển thị bất cứ thứ gì: hiển thị paywall có mục tiêu nếu attribution về trong khoảng thời gian chờ ngắn, hoặc paywall theo đối tượng mặc định nếu không có. AdaptyProfile.appliedExternalAttributionProviders cho bạn biết khi nào attribution từ AA đã được áp dụng.
Thuộc tính này chỉ báo cáo Apple Ads. Attribution từ các nhà cung cấp khác vẫn hiển thị trên hồ sơ người dùng và có thể dùng trong bộ lọc phân khúc, nhưng chưa hiển thị ở đây.
Trước khi bắt đầu
Bạn cần:
- Adapty Flutter SDK 4.1 trở lên. Với phiên bản 4.0.x, thuộc tính profile có tên là
appliedAttributionSourcesvà các giá trị của nó làAdaptyAttributionSource— xem Migrate to v4.1. Với phiên bản 3.17.0–3.x, flow cũng được tải bằnggetPaywall/getPaywallForDefaultAudiencevà kiểu trả về làAdaptyPaywall— xem Migrate to v4.0. - Apple Ads đã được cấu hình cho ứng dụng trong Adapty. Xem Apple Ads.
Cách hoạt động
Sau khi gọi Adapty().activate(), SDK sẽ yêu cầu dữ liệu attribution từ Apple Ads ở chế độ nền và chuyển kết quả đó về backend của Adapty. Khi AA trở thành nguồn attribution đang hoạt động cho hồ sơ người dùng, SDK sẽ gửi một AdaptyProfile đã được cập nhật đến listener didUpdateProfileStream của bạn, trong đó AdaptyExternalAttributionProvider.appleAds xuất hiện trong danh sách appliedExternalAttributionProviders.
Ở lần khởi động đầu tiên, có hai kết quả bạn cần xử lý:
- Attribution đến trong thời gian chờ. Gọi
getFlow— Adapty xử lý yêu cầu theo đối tượng Apple Ads và trả về paywall được nhắm mục tiêu. - Thời gian chờ hết trước. Hiển thị paywall của đối tượng mặc định thay thế, để người dùng không có attribution Apple Ads không phải chờ đợi.
getFlowForDefaultAudiencetrả về ngay mà không cần chờ phân khúc.
appliedExternalAttributionProviders có thể rỗng. Điều đó có nghĩa là một trong những trường hợp sau:
- Dữ liệu attribution của Apple Ads chưa được xử lý cho hồ sơ người dùng này.
- Chưa có dữ liệu attribution nào được ghi nhận.
- Dữ liệu attribution đến từ một nhà cung cấp khác, không được báo cáo trong mảng này.
Trong cả ba trường hợp, getFlowForDefaultAudience vẫn có thể gọi an toàn — hàm này trả về paywall theo đối tượng mặc định bất kể trạng thái hồ sơ người dùng là gì.
Việc chờ đợi chỉ áp dụng cho lần khởi chạy đầu tiên. Sau khi attribution từ Apple Ads đã được ghi lại, nó sẽ được lưu trữ vĩnh viễn trên hồ sơ người dùng. Ở mỗi lần khởi chạy tiếp theo, hồ sơ được lưu cache đã chứa AdaptyExternalAttributionProvider.appleAds trong appliedExternalAttributionProviders, nên đường dẫn attribution sẽ được xử lý ngay lập tức và getFlow trả về paywall theo phân khúc Apple Ads mà không có bất kỳ độ trễ nào.
Triển khai
Ở lần khởi chạy đầu tiên, hãy chờ AdaptyExternalAttributionProvider.appleAds và áp dụng timeout cứng — nếu attribution từ Apple Ads không bao giờ đến, những người dùng đó vẫn cần thấy paywall.
- Kích hoạt SDK. Xem Cài đặt & cấu hình Flutter SDK.
- Đăng ký nhận cập nhật hồ sơ người dùng với
Adapty().didUpdateProfileStream.listen(…). Nếu bạn chưa thiết lập listener, xem Lắng nghe cập nhật gói đăng ký. - Theo dõi
AdaptyExternalAttributionProvider.appleAdstrongappliedExternalAttributionProviders. Khi nó xuất hiện, tải paywall bằnggetFlow— Adapty sẽ trả về biến thể được phân khúc theo 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 là một broadcast stream và không replay, vì vậy hãy kiểm tra hồ sơ người dùng hiện tại một lần bằng getProfile(). Khi khởi động lại ứng dụng, attribution đã được lưu trữ sẽ không phát lại sự kiện.
- Khởi động bộ đếm thời gian 3–5 giây song song với subscription. Nếu bộ đếm kích hoạt trước khi
AdaptyExternalAttributionProvider.appleAdsxuất hiện, hãy tải paywall cho đối tượng mặc định bằnggetFlowForDefaultAudience. Hiển thị paywall nào resolve trước và hủy luồng còn lại, để tránh tải paywall hai lần. Cấu hình paywall dự phòng cho placement đó để người dùng không bị mắc kẹt nếu yêu cầu mạng thất bại.
Ví dụ hoàn chỉnh
Cách triển khai dưới đây chạy đua giữa attribution và timeout, tải trước paywall cho đối tượng mặc định song song, và trả về paywall phù hợp. Phía gọi hàm chỉ cần await một hàm duy nhất — không cần listener hay cờ trạng thái:
- Nếu attribution về kịp trong
timeout, hàm trả về paywall theo phân khúc thông quagetFlow. - Nếu
timeouthết trước, hàm trả về paywall đối tượng mặc định đã tải trước thông quagetFlowForDefaultAudience.
/// 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;
}
Gọi từ màn hình splash của bạn, sau đó hiển thị paywall khi promise được giải quyết:
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
}
Điều chỉnh timeout theo thời gian bạn muốn người dùng chờ trước khi paywall xuất hiện. Hầu hết người dùng không có attribution từ Apple Ads, nên họ sẽ chờ hết toàn bộ thời gian timeout — 3 đến 5 giây là mức cân bằng hợp lý. Attribution nếu có thường đến trong vài giây sau khi khởi động ứng dụng.
Nếu ứng dụng của bạn đã lắng nghe didUpdateProfileStream cho các mục đích khác (ví dụ: kiểm tra trạng thái gói đăng ký), bạn không cần thay đổi gì. didUpdateProfileStream là một broadcast stream, nên nó hỗ trợ nhiều listener độc lập mà không ảnh hưởng đến nhau.