Paywall Screen Implementation in Mobile Apps
Paywall is the only screen that directly impacts app revenue. Yet typical implementation is done last minute, two days before release. Result: a static screen with UILabel and UIButton that loads products for 2–3 seconds, doesn't work offline, and isn't testable via A/B testing. In our practice, we use a proven approach that guarantees stable performance and high conversion.
On a project with 1M installs, the Paywall took 5 seconds to load — conversion dropped by 30%. After implementing prefetch and caching, load time dropped to 0 and conversion increased by 25%. This isn't an isolated case: according to our measurements, each second of delay reduces conversion by 10–15%.
Why Prefetch Products Is Critical for Conversion
StoreKit 2 Product.products(for:) and BillingClient.queryProductDetailsAsync() are asynchronous requests to Apple/Google servers. In sandbox they sometimes take 3–5 seconds. In production they are usually faster, but not instant. If you load products only when the Paywall opens, the user sees a spinner or empty screen.
The right solution: prefetch products at app startup in AppDelegate.didFinishLaunching / Application.onCreate, cache in memory via ProductsCache singleton. The Paywall opens with pre-loaded data. Cache invalidation — on SKPaymentTransactionObserver.paymentQueue(_:updatedTransactions:) or via BillingClient.BillingClientStateListener.onBillingSetupFinished.
StoreKit 2 on iOS 15+:
// Prefetch at startup Task { ProductsCache.shared.products = try? await Product.products(for: productIDs) } // Paywall opens with cached data let products = ProductsCache.shared.products ?? [] | Approach | Load Time | Offline Support | A/B Test Flexibility |
|---|---|---|---|
| Without prefetch | 2–5 s | No | No |
| With prefetch | 0 s | Yes (cache) | Yes (Remote Config) |
Paywall Screen Structure
Minimum required elements:
- Value proposition — exactly what the user gets (not "premium access" but a list of features with icons).
- Plan options (monthly / yearly / lifetime) with a highlighted recommended plan.
- CTA button with amount and period.
- Restore Purchases link (required by App Store Guidelines 3.1.1).
- Terms of Use / Privacy Policy links (mandatory for subscription apps).
- Trial badge ("7 days free") if an introductory offer exists.
App Store Review Guidelines Section 3.1.1 — purchase restoration is mandatory for all subscription apps.
Trial offer. StoreKit 2 introductoryOffer — check via product.subscription?.isEligibleForIntroOffer (async, requires authenticated user). If eligible — show trial CTA. If not (already redeemed) — show standard price without trial messaging, otherwise the user expects a trial and gets angry at first charge.
How Animations and Design Affect Conversion
Switching between plans (monthly ↔ yearly) with animation of price recalculation — withAnimation(.spring()) in SwiftUI / animateContentChange in Compose. When selecting the yearly plan, show "Save 40%" with a strikethrough price for 12 months. This is A/B tested — sometimes "2 months free" converts better than a percentage discount. Average subscription value in such configurations increases by 20–30%.
Background gradients, images, Lottie animations — load them before the Paywall opens (prefetch) to avoid lag during display. On iOS, the Paywall is often presented modally with presentationDetents (half-sheet) — this improves conversion compared to full-screen for some categories.
Purchase Handling
// StoreKit 2 let result = try await product.purchase() switch result { case .success(let verification): switch verification { case .verified(let transaction): await transaction.finish() await EntitlementManager.shared.refresh() dismiss() case .unverified: showError("Purchase verification failed") } case .userCancelled: break // silently, don't show error case .pending: showPendingMessage() // purchase pending (Ask to Buy) } userCancelled — don't show error. The user closed it themselves — aggressive retry irritates and leads to 1-star ratings.
A/B Testing via Remote Config
Paywall is the prime candidate for A/B tests. Firebase Remote Config or RevenueCat Experiments: different prices, different trial lengths, different visual design. Changes without releasing a new version. Minimal implementation: Paywall configured via JSON from Remote Config (variant_id, trial_days, highlighted_plan), client renders according to config.
How to test Paywall in sandbox
Use Sandbox tester in App Store Connect and test accounts in Google Play. Ensure trial offer and introductory prices display correctly. After a sandbox purchase, always call restore to verify restoration logic.What's Included in the Work
We provide the complete package: Paywall source code with prefetch, purchase handling, restore, and trial offer; Remote Config integration for A/B tests; code and subscription scheme documentation; setup access to App Store Connect and Google Play Console; training your team on Paywall operation; and one month of support after delivery.
Why Entrust Paywall to Professionals?
Our experience — 5+ years in mobile app development, over 50 implemented projects with subscriptions. We guarantee Paywall stability and compliance with App Store and Google Play guidelines. Contact us for a consultation — we'll estimate cost and timeline for your project.
Timeline Estimates
Paywall with product prefetch, full purchase handling, restore, trial offer, and Remote Config A/B configuration — 2–3 business days given a ready StoreKit/Play Billing setup.
| Component | Time (days) |
|---|---|
| Product prefetch and caching | 0.5 |
| Paywall UI (SwiftUI / Compose) | 1 |
| Purchase handling, restore, trial | 1 |
| Remote Config integration | 0.5 |
| Testing and deployment | 0.5 |
Total time: from 2 to 3 business days depending on design complexity and number of A/B variants.
Get a consultation from our monetization expert — we'll assess your project and propose the optimal solution.







