How to Add a Paywall to a SwiftUI App
A hand-rolled RevenueCat paywall in SwiftUI — fetch offerings, wire the purchase and restore flow, and gate your premium feature behind an entitlement. Or skip the plumbing with The Swift Kit.
To add a paywall to a SwiftUI app, integrate the RevenueCat Purchases SDK, configure it on launch with your public API key, fetch the current offering, and render its packages in a custom SwiftUI view. Wire each package to Purchases.shared.purchase(package:), handle the user-cancelled case, add a Restore Purchases button, and unlock content by checking customerInfo.entitlements for your active entitlement. The plumbing is roughly a day of work to build cleanly yourself, or it ships pre-wired in The Swift Kit's RevenueCat module.
Why hand-roll it at all?
Building the paywall yourself is the best way to actually understand the RevenueCat model — offerings, packages, entitlements, and the customerInfo object that is the single source of truth for who paid. You will hit the real edge cases: a purchase that succeeds but the entitlement reads stale, the userCancelled result that looks like an error, restores that need to work before login, and prices that must come from localizedPriceString rather than hardcoded strings. None of that is hard once you have seen it. The cost is time and the maintenance of glue code you now own forever — config lifecycle, error states, loading states, and keeping the SDK current across major versions.
- Offerings let you change pricing remotely without an app update
- Entitlements (not product IDs) are what you gate features on
- customerInfo updates can arrive asynchronously — subscribe to them
- Always read prices from the SDK so currencies and intro offers stay correct
The honest trade-off vs. using the kit
If your goal is to learn StoreKit and RevenueCat deeply, hand-roll it — this guide is enough to get a working, App Store-compliant paywall. If your goal is to ship a monetized app this week, re-implementing the same purchase-and-restore plumbing every project is wasted motion. The Swift Kit's RevenueCat module is that plumbing, already wired into a centralized DesignSystem so the paywall matches your app's theme in one file, with the API key proxied through config rather than scattered in source. It is a $99 one-time purchase with lifetime updates, so SDK major-version bumps are maintained for you. The trade is the usual one: own the code and the upkeep, or buy a tested baseline and spend your time on the product.
Hand-roll a RevenueCat paywall in SwiftUI
This builds a paywall from scratch with the RevenueCat Purchases SDK — no RevenueCatUI, no templates, so you understand every line. If you would rather not own the boilerplate, the last step shows the shortcut.
- 1
Set up products in App Store Connect and RevenueCat
Create your subscription products (e.g. monthly, annual) in App Store Connect, then add them to a RevenueCat project. Create an entitlement called pro and attach it to those products, then group them under an offering called default. The offering is what your app fetches at runtime, so you can swap pricing without an app update.
- 2
Install the Purchases SDK
Add RevenueCat via Swift Package Manager. Point Xcode at the GitHub repo and add the Purchases product to your app target.
// File > Add Package Dependencies // https://github.com/RevenueCat/purchases-ios import RevenueCat - 3
Configure RevenueCat on app launch
Configure the SDK once, as early as possible, with your public API key. Keep the key out of source control — in The Swift Kit it lives in the centralized config, never hard-coded inline.
@main struct MyApp: App { init() { Purchases.logLevel = .debug Purchases.configure(withAPIKey: "appl_YOUR_PUBLIC_KEY") } var body: some Scene { WindowGroup { ContentView() } } } - 4
Fetch the offering and render packages
Load the current offering and lay out its packages in a SwiftUI list. Each package carries a localizedPriceString, so prices, currencies, and intro offers are formatted correctly per-store without you doing math.
@MainActor final class PaywallModel: ObservableObject { @Published var packages: [Package] = [] func load() async { let offerings = try? await Purchases.shared.offerings() packages = offerings?.current?.availablePackages ?? [] } } - 5
Wire the purchase flow
On tap, call purchase(package:) and inspect the result. The most-missed case is userCancelled — treat it as a no-op, not an error, or you will throw alerts at people who simply changed their mind.
func buy(_ package: Package) async { do { let result = try await Purchases.shared.purchase(package: package) if result.userCancelled { return } isPro = result.customerInfo.entitlements["pro"]?.isActive == true } catch { // surface a friendly retry message } } - 6
Add Restore Purchases and gate the feature
Apple requires a visible Restore button. Call restorePurchases(), then gate premium UI on the entitlement — read customerInfo.entitlements["pro"]?.isActive and also listen for updates so a purchase on another device unlocks here too.
// Restore let info = try await Purchases.shared.restorePurchases() isPro = info.entitlements["pro"]?.isActive == true // Gate if isPro { ProFeatureView() } else { PaywallView() } - 7
Or skip the plumbing with The Swift Kit
Everything above — config, offerings fetch, purchase and restore flow, the userCancelled trap, entitlement gating, and a themeable paywall screen — is pre-wired in The Swift Kit's RevenueCat module. You run ./setup.sh, drop in your API key, flip the feature flag, and ship. Use it as a reference build or as your starting point.
Frequently Asked Questions
Do I need RevenueCatUI to add a paywall, or can I build my own?
Why does my SwiftUI paywall show no products?
How do I unlock the feature after purchase?
Do I have to add a Restore Purchases button?
Is it faster to use The Swift Kit instead of building this myself?
Keep exploring
Skip the paywall plumbing
The Swift Kit ships the full RevenueCat flow — offerings, purchase, restore, and entitlement gating — pre-wired and themed by a central design system. $99 one-time, lifetime updates, 14-day refund. Build it yourself to learn, or start here to ship this week.
Get The Swift Kit — $99One-time purchase · Lifetime updates · 14-day refund