A complete StoreKit 2 wrapper in ~150 readable lines. Product loading, purchase, verification, restore, live transaction updates, entitlement gating — no RevenueCat, no third-party SDKs, no per-transaction fees.
import PaywallKit
// One store per app. Product IDs come from App Store Connect.
@State private var store = PaywallStore(productIDs: ["pro.monthly", "pro.yearly"])Gate content anywhere:
if store.hasEntitlement {
ProFeatureView()
} else {
Button("Unlock Pro") { showPaywall = true }
}In Xcode: File → Add Package Dependencies… and paste:
https://github.com/aligutierrez/PaywallKit
Or in Package.swift:
.package(url: "https://github.com/aligutierrez/PaywallKit.git", from: "1.0.0")PaywallStore—@Observable,@MainActor. Fetches products (in your ID order), purchases with StoreKit 2's built-in verification, restores, listens toTransaction.updatesfor the app's lifetime, and exposeshasEntitlementfor gating.PaywallPlan— a plain display model built from real products: name, price, period, trial ("7-day free trial"), and an automatic "Save X%" badge when you sell both a 1-month and a yearly plan.PaywallStore.preview(plans:)— fixed plans, no App Store connection. Your paywall previews render without StoreKit setup.- Offline-safe entitlements — verified entitlements are cached, so a StoreKit flake (offline,
ASDErrorDomain 500) never locks a paying user out. Real refunds still revoke viaTransaction.updates. - App Store promoted purchases —
PurchaseIntent.intentshandled automatically (iOS 16.4+/macOS 14.4+). - Ask to Buy — parental-approval purchases surface a pending message instead of failing silently;
AppStore.canMakePaymentsis checked before purchasing. - Unit tests included.
// Load products (call from .task or onAppear)
await store.load()
// store.plans is ready for your UI
ForEach(store.plans) { plan in
Text("\(plan.name) — \(plan.price)")
}
// Purchase and restore
let success = await store.purchase(planID: "pro.yearly")
await store.restore()Test offline with an Xcode StoreKit Configuration file: File → New → File… → StoreKit Configuration File, then select it under Product → Scheme → Edit Scheme → Run → Options.
Requirements: iOS 17+ / macOS 14+, Swift 5.9+. Zero dependencies.
This is the engine. PaywallKit is the same module plus three production-ready SwiftUI paywall templates — drop-in upgrade, same package name:
- 🏆 Hero — symbol, headline, feature list, plan picker
- 📊 Free-vs-Pro comparison — for freemium apps
- ⏰ Trial timeline — the Blinkist-style pattern with the best trial-start rates in the industry
- Localized in English, German, and Spanish; App Review requirements (restore, terms/privacy) built into every template
- One config object for all three → A/B test layouts by changing one word
MIT — see LICENSE.
