iOS SDK'da ilk başlatmada AA hedefli paywall gösterme

Apple Ads (AA) attribution, Adapty.activate() çağrısından sonra asenkron olarak gelir. getPaywall fonksiyonunu erken çağırırsanız, attribution henüz gelmemiş olabilir ve Adapty, placement’ı varsayılan kitle üzerinden çözümleyerek AA segmentine göre ayarlanmış paywallları atlayabilir. AdaptyProfile.appliedAttributionSources, uygulamanın AA attribution’ının profile uygulanıp uygulanmadığını tespit etmesini sağlar; böylece paywall isteği, AA segmentasyonunun doğru şekilde çözümleneceği ana kadar bekletilebilir.

Başlamadan önce

İhtiyacınız olanlar:

  • Adapty iOS SDK 3.17.1 veya üzeri.
  • Uygulamanız için Adapty’de Apple Ads yapılandırılmış olması. Bkz. Apple Ads.

Nasıl çalışır

Adapty.activate() çağrısının ardından SDK, arka planda Apple’dan Apple Ads attribution bilgisini talep eder ve sonucu Adapty’nin backend’ine iletir. AA, profil için aktif attribution kaynağı haline geldiğinde SDK, appliedAttributionSources dizisinde .appleAds içeren güncellenmiş bir AdaptyProfile döndürür.

Boş bir dizi şu anlara gelebilir:

  • Bu profil için Apple Ads attribution henüz işlenmemiş olabilir.
  • Hiç attribution gelmemiş olabilir. Boş bir dizi ile bile getPaywall çağrısı güvenlidir — Adapty, isteği mevcut profil durumuna uyan kitleye göre (genellikle varsayılan kitle) çözümler.

Bekleme yalnızca ilk başlatma için geçerlidir. Apple Ads attribution kaydedildikten sonra profilde kalıcı olarak saklanır. Sonraki her başlatmada önbelleğe alınmış profil zaten appliedAttributionSources içinde .appleAds değerini taşır, didLoadLatestProfile bu değerle anında tetiklenir ve getPaywall herhangi bir gecikme olmadan Apple Ads segmentine uygun paywall’ı döndürür.

Uygulama

İlk açılışta profilde .appleAds değerini izleyin ve sert bir zaman aşımı uygulayın — Apple Ads attribution hiç gelmese bile, o kullanıcıların yine de bir paywall görmesi gerekir.

  1. SDK’yı etkinleştirin. Bkz. iOS SDK’yı yükle ve yapılandır.
  2. Profil güncellemelerini dinleyin: AdaptyDelegate’e uyum sağlayarak didLoadLatestProfile’ı uygulayın. Delegate’i henüz ayarlamadıysanız bkz. Abonelik güncellemelerini dinle.
  3. appliedAttributionSources içinde .appleAds çıkıp çıkmadığını takip edin. Göründüğünde paywall’ı isteyin — Adapty, AA segmentine göre belirlenmiş varyantı döndürecektir:
extension <YourAdaptyDelegateImpl>: AdaptyDelegate {
    nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) {
        if profile.appliedAttributionSources.contains(where: { $0 == .appleAds }) {
            // load paywall via Adapty.getPaywall(placementId:)
        }
    }
}
  1. Abonelikle paralel olarak 3–5 saniyelik bir zamanlayıcı başlatın. Zamanlayıcı .appleAds görünmeden tetiklenirse, yine de paywallı isteyin: Hangi yol önce tetiklenirse o ywall’ı yüklemeli; diğer yol atlanmalıdır. Paywall’ın iki kez getirilmemesi için tek bir durum bayrağı (örneğin hasLoadedPaywall) kullanarak yinelenenleri ortadan kaldırın. Ağ isteği başarısız olduğunda kullanıcının askıda kalmaması için placement’a bir yedek paywall yapılandırın.

Eksiksiz örnek

Aşağıdaki implementasyon, attribution ile bir zaman aşımını yarıştırır, varsayılan kitle paywallını paralel olarak önceden çeker ve uygun olan paywallı döndürür. Çağıran taraf yalnızca tek bir async fonksiyonu bekler; çağrı noktasında yönetilmesi gereken delegate veya durum bayrağı yoktur.

ProfileObserver, AdaptyDelegate’ten gelen profil güncellemelerini yayınlayan yeniden kullanılabilir bir singleton’dır. PaywallLoader.getPaywallOrDefault ise yarışı yapılandırılmış eşzamanlılık TaskGroup kullanarak yürütür:

  • Attribution timeout süresi içinde gelirse, getPaywall(placementId:) aracılığıyla segmentlere ayrılmış paywall döndürülür.
  • timeout süresi önce dolarsa, getPaywallForDefaultAudience(placementId:) aracılığıyla önceden yüklenmiş varsayılan kitle paywall’u döndürülür.

/// Attribution uygulanmasına bağlı bir paywallın nasıl getirileceğini gösterir;
/// attribution zamanında gelmezse varsayılan kitle paywallına geri düşer.
///
/// Durumsuz ve bağımsızdır: her çağrı kendi varsayılan kitle ön getirme işlemini
/// başlatır ve bunu attribution + segmentli getirme ile yarıştırır.
enum PaywallLoader {
    static func getPaywallOrDefault(
        placementId: String,
        timeout: TimeInterval
    ) async throws -> AdaptyPaywall {
        struct TimedOut: Error {}

        // Varsayılan kitle isteğini hemen başlat; böylece yüklemek için tam
        // `timeout` süresi olur. Başarı durumunda iptal edilir, zaman aşımında
        // sonucu beklenir — hiçbir zaman yinelenen ağ çağrısı yapılmaz.
        let defaultPaywallTask = Task {
            try await Adapty.getPaywallForDefaultAudience(placementId: placementId)
        }

        do {
            // İki alt görevi yarıştır: hangisi önce biterse o kazanır.
            let result = try await withThrowingTaskGroup(of: AdaptyPaywall.self) { group in
                // 1. Attribution'ı bekle, ardından Adapty'den segmentli paywallı iste.
                group.addTask {
                    await waitForAttribution()
                    return try await Adapty.getPaywall(placementId: placementId)
                }
                // 2. Zaman bombası: `timeout` saniye sonra `TimedOut` fırlatır.
                group.addTask {
                    try await Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000))
                    throw TimedOut()
                }
                guard let value = try await group.next() else { throw CancellationError() }
                group.cancelAll() // Kaybedeni durdur (uyuyan görev veya attribution bekleyen).
                return value
            }
            // Segmentli paywall kazandı — varsayılan kitle ön getirme artık gerekli değil.
            defaultPaywallTask.cancel()
            return result
        } catch is TimedOut {
            // Attribution zamanında uygulanamadı — önceden yüklenen varsayılanı döndür
            // (zaten tamamlandıysa anında, değilse devam eden isteği bekle).
            return try await defaultPaywallTask.value
        }
    }

    /// İstenen attribution kaynağına sahip bir profil gözlemlenene kadar askıya alır.
    /// `@Published.values`, abonelikte mevcut profili hemen yayımlar; bu nedenle
    /// attribution zaten uygulanmışsa ilk iterasyonda geri döner.
    @MainActor
    private static func waitForAttribution() async {
        for await profile in ProfileObserver.shared.$profile.values {
            if profile?.appliedAttributionSources.contains(.appleAds) == true { return }
        }
    }
}

@MainActor
final class ProfileObserver: AdaptyDelegate {
    static let shared = ProfileObserver()

    @Published private(set) var profile: AdaptyProfile?

    nonisolated func didLoadLatestProfile(_ profile: AdaptyProfile) {
        Task { @MainActor [weak self] in
            self?.profile = profile
        }
    }
}

Adapty.activate() tamamlandıktan sonra ProfileObserver’ı AdaptyDelegate’e bir kez bağlayın:

Adapty.delegate = ProfileObserver.shared

Splash ekranından çağırın:

do {
    let paywall = try await PaywallLoader.getPaywallOrDefault(
        placementId: "YOUR_PLACEMENT_ID",
        timeout: 5
    )
    // paywallı göster
} catch {
    // hatayı yönet veya yedek paywall göster
}

Uygulamanız başka amaçlar için zaten bir AdaptyDelegate kullanıyorsa (örneğin, abonelik güncellemelerini dinlemek için), Adapty.delegate = ProfileObserver.shared yapmak yerine mevcut delegate’inizden didLoadLatestProfile’ı ProfileObserver.shared’a iletin.