NewThe Flutter Kit — Flutter boilerplate$149$69
Guide

iOS Paywall Design Patterns 2026: Five Archetypes That Convert

The five paywall archetypes that work in 2026 iOS apps. Conversion psychology, working SwiftUI code for each, App Review rejection patterns, A/B testing without app updates, and the right placement strategy for your category.

Ahmed GaganAhmed Gagan
16 min read

Skip 100+ hours of setup. Get The Swift Kit $149 $99 one-time

Get it now →

The 30-second answer

Five paywall archetypes cover most shipped iOS subscription apps in 2026: single product with free trial, tiered comparison, hard paywall on first launch, soft paywall feature gate, and free trial first. Annual plan default selected lifts annual share by 30 to 60 percent. A 7 day trial with explicit no payment language lifts trial start rate by 20 to 40 percent. Place the paywall after a clear value moment, not on first launch with no value shown.

Paywalls are the highest leverage screen in any subscription iOS app. A 5 percent conversion lift compounds into 50 percent more revenue over a year. This guide covers the five archetypes that work in 2026, with SwiftUI code for each, the conversion psychology behind why they work, the most common App Review rejection reasons, and how to A/B test without shipping app updates.

The Five Paywall Archetypes

ArchetypeDescriptionBest for
1. Single ProductOne plan, one price, optional free trialUtilities, focused tools, minimum viable monetization
2. Tiered ComparisonMonthly vs annual columns with savings badgeEstablished apps maximizing annual share
3. Hard PaywallNo free tier, paywall on first launchPremium content apps, high conviction value
4. Soft PaywallApp is usable, premium features lockedProductivity, fitness, journaling
5. Free Trial FirstTrial starts automatically, paywall at trial endHigh value subscription apps, post onboarding

Archetype 1: Single Product Paywall

The simplest pattern. One product, one price, optional free trial. Used by Bear, Things, Tot, and many indie productivity apps.

struct SingleProductPaywall: View {
    @Environment(SubscriptionStore.self) private var store
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        VStack(spacing: 24) {
            Image(systemName: "sparkles")
                .font(.system(size: 60))
                .foregroundStyle(.tint)

            VStack(spacing: 8) {
                Text("Pro").font(.largeTitle.bold())
                Text("Unlock everything")
                    .font(.headline)
                    .foregroundStyle(.secondary)
            }

            VStack(alignment: .leading, spacing: 12) {
                FeatureRow(icon: "checkmark", text: "Unlimited notes")
                FeatureRow(icon: "checkmark", text: "iCloud sync")
                FeatureRow(icon: "checkmark", text: "Custom themes")
                FeatureRow(icon: "checkmark", text: "Priority support")
            }
            .padding()
            .background(.regularMaterial, in: .rect(cornerRadius: 12))

            Spacer()

            VStack(spacing: 8) {
                Text("Try free for 7 days")
                    .font(.caption)
                    .foregroundStyle(.secondary)

                Button {
                    Task { await store.purchase(annualPackage) }
                } label: {
                    VStack(spacing: 4) {
                        Text("Start Free Trial")
                            .font(.headline)
                        Text("then 39.99 USD per year")
                            .font(.caption)
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 14)
                }
                .buttonStyle(.borderedProminent)
                .controlSize(.large)
            }

            Button("Restore Purchases") {
                Task { await store.restore() }
            }
            .font(.caption)
            .foregroundStyle(.secondary)
        }
        .padding()
        .overlay(alignment: .topTrailing) {
            Button { dismiss() } label: {
                Image(systemName: "xmark.circle.fill")
                    .font(.title2)
                    .foregroundStyle(.tertiary)
            }
            .padding()
        }
    }
}

What makes this pattern work: clear value list, single decisive CTA, explicit trial language, visible restore button, and an unambiguous close button. Apple App Review approves this archetype consistently when those five elements are present.

Archetype 2: Tiered Comparison Paywall

Two plans side by side, monthly and annual, with a "Save 50 percent" badge on the annual to anchor toward the higher value option. Used by Notion, Bear, Castro, and most subscription apps once they have traction.

enum BillingCycle: String, CaseIterable {
    case monthly, annual
}

struct TieredPaywall: View {
    @State private var selected: BillingCycle = .annual

    var body: some View {
        VStack(spacing: 24) {
            VStack(spacing: 8) {
                Text("Upgrade to Pro")
                    .font(.largeTitle.bold())
                Text("Unlock unlimited features")
                    .foregroundStyle(.secondary)
            }

            HStack(spacing: 12) {
                ForEach(BillingCycle.allCases, id: \.self) { cycle in
                    PlanCard(
                        cycle: cycle,
                        isSelected: cycle == selected,
                        onTap: { selected = cycle }
                    )
                }
            }

            FeatureList()

            Button("Continue") {
                Task { await purchase(selected) }
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)
            .frame(maxWidth: .infinity)

            Button("Restore Purchases") { Task { await restore() } }
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}

struct PlanCard: View {
    let cycle: BillingCycle
    let isSelected: Bool
    let onTap: () -> Void

    var body: some View {
        VStack(spacing: 8) {
            if cycle == .annual {
                Text("BEST VALUE")
                    .font(.caption2.bold())
                    .foregroundStyle(.white)
                    .padding(.vertical, 2)
                    .padding(.horizontal, 8)
                    .background(.green, in: .capsule)
            }

            Text(cycle == .monthly ? "Monthly" : "Annual")
                .font(.headline)

            Text(cycle == .monthly ? "9.99" : "39.99")
                .font(.title.bold())

            Text(cycle == .monthly ? "USD per month" : "USD per year")
                .font(.caption)
                .foregroundStyle(.secondary)

            if cycle == .annual {
                Text("Save 67 percent")
                    .font(.caption2.bold())
                    .foregroundStyle(.green)
            }
        }
        .frame(maxWidth: .infinity)
        .padding()
        .background(
            RoundedRectangle(cornerRadius: 16)
                .fill(isSelected ? Color.accentColor.opacity(0.15) : Color.gray.opacity(0.08))
                .overlay {
                    RoundedRectangle(cornerRadius: 16)
                        .stroke(isSelected ? Color.accentColor : Color.clear, lineWidth: 2)
                }
        )
        .onTapGesture(perform: onTap)
    }
}

Default the annual plan selected. Three measured wins from the indie iOS A/B testing community: 30 to 60 percent more annual subscriptions vs monthly default, 8 percent overall trial start rate lift, and significantly higher LTV per subscriber thanks to lower annual churn.

Archetype 3: Hard Paywall on First Launch

The app is gated entirely behind a paywall on first launch. Used by Calm, Headspace, Tot, and many high conviction premium content apps. Effective when your value proposition is obvious and the App Store screenshots have already done the selling.

struct HardPaywall: View {
    var body: some View {
        ZStack {
            // Background that previews the experience
            Image("paywall_hero")
                .resizable()
                .scaledToFill()
                .ignoresSafeArea()

            LinearGradient(
                colors: [.clear, .black.opacity(0.85)],
                startPoint: .top,
                endPoint: .bottom
            )
            .ignoresSafeArea()

            VStack {
                Spacer()
                VStack(alignment: .leading, spacing: 16) {
                    Text("Master your habits")
                        .font(.largeTitle.bold())
                        .foregroundStyle(.white)

                    Text("Join 1.2 million people building lasting habits")
                        .foregroundStyle(.white.opacity(0.85))

                    HStack {
                        Text("⭐⭐⭐⭐⭐")
                        Text("4.9 from 50,000 reviews")
                            .foregroundStyle(.white.opacity(0.8))
                            .font(.caption)
                    }

                    Button {
                        Task { await purchase() }
                    } label: {
                        Text("Start 7 day free trial")
                            .font(.headline)
                            .frame(maxWidth: .infinity)
                            .padding(.vertical, 14)
                    }
                    .buttonStyle(.borderedProminent)

                    Text("Then 49.99 USD per year. Cancel anytime.")
                        .font(.caption2)
                        .foregroundStyle(.white.opacity(0.6))

                    HStack {
                        Button("Terms of Service") { showTerms() }
                        Spacer()
                        Button("Privacy Policy") { showPrivacy() }
                        Spacer()
                        Button("Restore") { Task { await restore() } }
                    }
                    .font(.caption2)
                    .foregroundStyle(.white.opacity(0.6))
                }
                .padding()
            }
        }
    }
}

Hard paywalls require strong upstream marketing. The App Store page must already convince. The paywall reinforces with social proof (review count, star rating) and removes friction (one tap to free trial). Apple App Review accepts hard paywalls when the trial terms are explicit and Restore plus Terms plus Privacy links are visible.

Archetype 4: Soft Paywall Feature Gate

The app is fully usable with a free tier. Premium features are gated and trigger a paywall on first attempt. Used by Bear, Day One, Drafts, and most productivity tools. Lower trial start rate but stronger retention because users have already invested in the app.

struct CreateDocumentView: View {
    @Environment(SubscriptionStore.self) private var store
    @State private var showPaywall = false

    let documentCount: Int
    let documentLimit = 3

    var canCreate: Bool {
        store.isPro || documentCount < documentLimit
    }

    var body: some View {
        Button {
            if canCreate {
                createDocument()
            } else {
                showPaywall = true
            }
        } label: {
            Label("New Document", systemImage: "plus.circle.fill")
        }
        .sheet(isPresented: $showPaywall) {
            FeatureGatePaywall(reason: .documentLimit)
        }
    }
}

struct FeatureGatePaywall: View {
    let reason: PaywallReason

    var body: some View {
        VStack(spacing: 24) {
            Image(systemName: "doc.fill")
                .font(.system(size: 60))
                .foregroundStyle(.tint)

            Text("You have hit the limit")
                .font(.title2.bold())

            Text("Free accounts can have 3 documents. Upgrade to Pro for unlimited.")
                .multilineTextAlignment(.center)
                .foregroundStyle(.secondary)

            // Plans here
            // ...
        }
        .padding()
    }
}

The reason argument lets you customize the headline based on which feature triggered the paywall. Users who hit a feature gate convert at significantly higher rates than users browsing settings, because they have a specific use case in mind.

Archetype 5: Free Trial First

The trial starts automatically on app launch (no purchase required) and the paywall appears at trial end. Used by Calm and the Calm style meditation apps. Strong retention because the user experiences the full value during the trial.

struct AppRoot: View {
    @AppStorage("trialStartDate") var trialStartDate: Date?
    @AppStorage("trialDays") var trialDays: Int = 7
    @Environment(SubscriptionStore.self) private var store

    var trialIsActive: Bool {
        guard let start = trialStartDate else { return false }
        return Date().timeIntervalSince(start) < TimeInterval(trialDays * 86400)
    }

    var body: some View {
        Group {
            if store.isPro || trialIsActive {
                MainAppView()
            } else {
                TrialEndedPaywall()
            }
        }
        .task {
            if trialStartDate == nil {
                trialStartDate = Date()
            }
        }
    }
}

A few things to handle correctly. One, persist the trial start date across reinstalls if your goal is preventing re trial abuse (use iCloud Key Value Store). Two, show the trial countdown in settings or a banner so users know when it ends. Three, send a push notification 24 hours before trial end (lift conversion 10 to 20 percent).

Conversion Psychology in 2026

Five patterns with measurable lift across indie iOS apps:

  1. Annual plan default selected. Lifts annual share by 30 to 60 percent vs monthly default. Annual subscribers churn 70 percent less than monthly.
  2. Most Popular or Best Value badges. Lift conversion 5 to 15 percent on the recommended plan. Pair with a discount or trial.
  3. 3 to 7 day free trial with no payment language. Lift trial start rate 20 to 40 percent over no trial. Be explicit: "Free for 7 days. No payment needed today."
  4. Inline social proof. "4.8 stars, 50,000 happy users" near the CTA. Use real numbers from your App Store metadata.
  5. Single primary CTA. One big button outperforms two side by side options most of the time. Reduce decision fatigue.

Test in your category. The pattern that wins for a meditation app may lose for a fitness tracker. Use RevenueCat Offerings or Superwall to A/B test without ship cycles.

App Review Rejection Patterns

The six most common reasons paywalls get rejected:

  • Missing or unclear Restore Purchases button. Visible on every paywall, ideally as a tertiary button under the primary CTA.
  • Free trial terms not explicit. Must say "Free for X days, then Y USD per period."
  • Auto renewable subscription terms not visible. Must show price, period, link to terms and privacy.
  • Requesting payment outside StoreKit. Only allowed via External Purchase Link entitlement (EU) or alternative payment options on macOS. Default iOS apps must use StoreKit.
  • Pro features not delivered post purchase. Implement entitlement checks correctly. Apple verifies post purchase that the advertised features unlock.
  • Dark patterns. Hiding the close button, requiring a countdown to dismiss, or making the close button intentionally hard to tap.

Fix all six before submission. Use the App Review checklist in your release process.

Paywall Placement

Three valid placements. Pick based on your category and user value perception:

PlacementTrial start rateBest for
End of onboarding (3 to 5 value screens, then trial)8 to 25 percentHard paywall apps
After clear value moment (saved 5 notes, scanned 3 docs)5 to 15 percentSoft paywall apps
First attempt at premium feature10 to 30 percentFeature gated apps
First launch, no value shown2 to 5 percentAvoid (also App Review risk)

The Calm and Headspace pattern of value first, paywall second produces 2 to 3 times higher trial start rates than first launch with no preview. Build trust before asking for the trial.

A/B Testing Without App Updates

Three approaches:

  1. RevenueCat Offerings. Configure multiple paywall variants in the dashboard. The SDK serves a random variant. Read the variant ID and render the matching SwiftUI view. The simplest path; works with any paywall design.
  2. Superwall. Paywalls are server side templates. The entire UI is hot swappable. Costs 90 to 200 dollars per month minimum but ships the most flexible no code editor on the market.
  3. Custom server driven UI. Your backend returns a JSON paywall description. Your SwiftUI paywall view interprets the JSON. Most flexible but requires building and maintaining the rendering layer.

Most indie apps start with RevenueCat Offerings, graduate to Superwall when paywall experimentation becomes the primary growth lever.

Real World Examples (What Top Apps Ship)

  • Calm: Hard paywall after onboarding video. Annual default. Social proof ("Featured in NYT").
  • Bear: Soft paywall feature gate. Free tier usable, sync and themes locked. Annual default with monthly visible.
  • Headspace: Free trial first. Trial starts on launch, paywall at day 7 and again at day 14.
  • Tot: Single product paywall on first launch. One price, no trial. Simple and effective.
  • Things: No subscription. One time purchase only. Outlier in the category but maintains a loyal base.
  • NetNewsWire: No paywall. Free open source. Reference for what users compare your paywall against.

The Swift Kit Ships All Five Archetypes

The Swift Kit ships every paywall archetype above as a reusable SwiftUI component. Single product paywall, tiered comparison, hard paywall, soft paywall feature gate, and free trial first. Each one is wired to RevenueCat and the design system tokens. Pick your archetype, edit the copy, ship.

Frequently Asked Questions

Should I build a custom paywall or use RevenueCat Paywalls v2?

Build custom if your paywall is core to your brand or you have multi step flows. Use RevenueCat Paywalls v2 if you want fast A/B testing and the templates fit. Many apps run a custom paywall plus Superwall for experimentation in parallel.

What is the average paywall conversion rate for indie iOS apps?

Rough benchmarks. Trial start rate (visit to trial start): 5 to 25 percent depending on archetype. Trial to paid conversion: 30 to 60 percent depending on category. Visit to paid (skipping trial): 2 to 8 percent. The single biggest variable is paywall placement and the value the user has experienced before seeing it.

Do I need a paywall video?

Helpful for hard paywalls in lifestyle and content categories. Less impactful for utility and productivity apps where the value is obvious from screenshots. Test before investing in video production. A 15 to 30 second loop in the App Store preview often outperforms a paywall video.

Where to Go Next

Share this article
Limited-time · price rises to $149 soon

Ready to ship your iOS app faster?

The Swift Kit gives you a production-ready SwiftUI codebase with onboarding, paywalls, auth, AI integrations, and a full design system. Stop rebuilding boilerplate — start building your product.

$149$99one-time · save $50
  • Full source code
  • Unlimited projects
  • Lifetime updates
  • 50+ makers shipping