Paywall Kit — Plan Model, Purchase Actions & Motion for SWPaywall Templates
Shared base for the eleven SWPaywall templates — an SWPaywallPlan model that covers subscriptions of any period, lifetime unlocks, consumable packs and intro offers (free trial, pay-up-front, pay-as-you-go) and derives every price comparison and terms line from real numbers, a `.swPaywallActions(purchase:restore:termsURL:privacyURL:)` environment hook with shared purchase button, restore/legal footer and close button, and TimelineView/Canvas motion helpers (aurora, particles, Ken Burns, shine, entrance) — plus loaders that feed plans from StoreKit 2 or RevenueCat
Copies a short instruction with your private download link. Paste it into your agent: it downloads the whole skill (the source code, the setup steps and the mistakes we already made for you) and builds the screen.
Works with
What it does
Every custom paywall needs the same plumbing before it can look good: a model for "what can be bought" that handles weekly / monthly / quarterly / yearly / lifetime / consumable products, intro offers, per-month equivalents and "Save X%" badges; billing-terms copy that matches the real offer (App Review checks this); a purchase button that spins and locks the screen while StoreKit works; Restore Purchases, Terms and Privacy links; and a close button. This kit is that plumbing, shared by every paywall-* template skill:
SWPaywallPlan— one purchasable option. Build it from a StoreKit 2Product(real, localized prices) or by hand (previews, demos). Templates read derived values only (billingText,termsText,callToAction,savings(in:),pricePerMonthText…), so nothing in a template is hard-coded to "monthly + yearly"..swPaywallActions(...)— attach your purchase / restore closures and legal URLs once; every template inside picks them up from the environment. Works the same for StoreKit 2, RevenueCat or your own backend: the closure receives the selected plan.- Building blocks —
SWPaywallPurchaseButton(spinner + lock whilepurchaseruns),SWPaywallFooter(Restore · Terms · Privacy),SWPaywallCloseButton(Liquid Glass on iOS 26),SWPaywallPanelBackground,SWPaywallPressStyle,SWPaywallFeature. - Motion —
SWPaywallAurora,SWPaywallParticles(snow / embers / stars),.swPaywallKenBurns(),.swPaywallShine(in:),.swPaywallEntrance(_:delay:). All driven byTimelineView+Canvas(no timers), all frozen under Reduce Motion.
What you get
- ✓1,201 lines of source code, ready to paste
- ✓Architecture
- ✓Dependencies
- ✓Implementation
- ✓Usage
- ✓Integration Checklist
- ✓Common Customizations
- ✓Known Pitfalls
Architecture
| File | Contents |
|---|---|
SWPaywallPlan.swift | SWPaywallPlan (+ Period, Kind, Offer), StoreKit mapping, derived prices and copy, sample* plans |
SWPaywallActions.swift | SWPaywallActions environment value + .swPaywallActions(...), SWPaywallFeature, purchase / close buttons, footer, panel background, press style |
SWPaywallEffects.swift | Aurora, particles, Ken Burns, shine, entrance |
SWDebugLog.swift | swDebugLog(...) — debug-only logging used by SWPaywallActions (skip it if your project already has ShipSwift's SWUtil) |
Template catalog (each is its own skill and "requires paywall-kit"):
| Skill | Layout | Plans it is built for |
|---|---|---|
paywall-plan | Apple SubscriptionStoreView with branded header (classic plan picker) | monthly + yearly |
paywall-timeline | "How your free trial works" timeline (Today → reminder → billing day) | one plan with a free trial |
paywall-hero | Hero photo, checklist, stacked plan rows | 2–3 subscriptions |
paywall-immersive | Full-bleed photo, rating laurels, dark glass rows | weekly + yearly |
paywall-editorial | Serif, paper-toned membership page | monthly + quarterly + yearly |
paywall-plan-cards | "1 / 12 / ∞" cards, award, review carousel | subscriptions + lifetime |
paywall-lifetime | Gold emblem, feature grid, price anchor | one lifetime unlock |
paywall-credits | Consumable packs with per-row price buttons | consumables |
paywall-tiers | Plus / Pro segmented tiers | 2+ tiers × monthly/yearly |
paywall-offer | Discount hero, ticket, countdown | one plan with an intro / win-back offer |
paywall-comparison | Free vs Pro table + billing switch | monthly + yearly |
How derived values work (all on SWPaywallPlan):
| Property / method | Example output |
|---|---|
displayPrice | $59.99 (StoreKit's localized string when built from a Product) |
billingText | $9.99/month, $24.99 every 3 months, $99.99 once |
pricePerMonthText / pricePerWeekText | $5.00 / $1.15 |
equivalentText(per: .month) | $5.00/mo |
savings(in: plans) | 50 — vs the most expensive comparable plan, per month (subscriptions) or per unit (consumables) |
offerText | 7-day free trial, $29.99 for the first year, $1.99/month for 3 months |
termsText | 7-day free trial, then $59.99/year. Cancel anytime. / Renews monthly at $9.99 until canceled. |
callToAction | Start 7-Day Free Trial, Claim My Offer, Subscribe for $9.99/month, Unlock Forever |
reassuranceText | No payment now. Cancel anytime. |
SWPaywallPlan.defaultID(in:preferred:) | preferred ID → first plan with a badge → best per-month subscription → first plan |
More in Paywall and payments










