reviews: ask unico a premium existentes, funnel instrumentado y fix source unknown del paywall

- requestForExistingPremiumUser(): prompt nativo una sola vez para quien
  ya era premium antes del ask post-compra (evento review_prompt_premium_user)
- Funnel de semanas completadas emite review_funnel_shown/positive/negative/dismissed
- Paywall de Home via sheet(item:) con PaywallPresentation: el source viaja
  con la presentacion; antes dos @State separados hacian que a veces se
  registrara paywall_viewed con source=unknown (post_onboarding nunca llegaba)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013H6bXqGX1ygwib1Dm3n3UG
This commit is contained in:
alexandrev-tibco
2026-08-26 14:33:18 +02:00
parent d41f72a84f
commit 26d00f65e0
2 changed files with 43 additions and 17 deletions
@@ -8,6 +8,7 @@ final class ReviewPromptService {
private let reviewCompletedKey = "review_funnel_completed"
private let lastPromptedCompletedWeeksKey = "review_funnel_last_prompted_completed_weeks"
private let postPurchaseAskedKey = "review_post_purchase_asked"
private let premiumUserAskedKey = "review_premium_user_asked"
private let minimumCompletedWeeksToStart = 1
private let promptIntervalWeeks = 2
@@ -22,6 +23,7 @@ final class ReviewPromptService {
/// user just paid, so sentiment is at its peak. Once per user; the small
/// delay lets the "premium active" state render before the system alert.
func requestAfterPurchase() {
guard !hasCompletedReviewFlow else { return }
guard !UserDefaults.standard.bool(forKey: postPurchaseAskedKey) else { return }
UserDefaults.standard.set(true, forKey: postPurchaseAskedKey)
AnalyticsService.logEvent("review_prompt_post_purchase")
@@ -32,6 +34,21 @@ final class ReviewPromptService {
}
}
/// One-time ask for users who were already premium before the post-purchase
/// prompt existed they never went through it, and a paying user is the
/// most likely 5-star reviewer the app has.
func requestForExistingPremiumUser() {
guard !hasCompletedReviewFlow else { return }
guard !UserDefaults.standard.bool(forKey: premiumUserAskedKey) else { return }
UserDefaults.standard.set(true, forKey: premiumUserAskedKey)
AnalyticsService.logEvent("review_prompt_premium_user")
Task { @MainActor in
try? await Task.sleep(for: .seconds(2))
requestReview()
markReviewCompleted()
}
}
func shouldShowFunnelAfterWeekCompletion(completedWeeks: Int) -> Bool {
guard !hasCompletedReviewFlow else { return false }
guard completedWeeks >= minimumCompletedWeeksToStart else { return false }