RevenueCat is the fastest way to ship in-app purchases on iOS without spending a week wiring up receipt validation, restore flows, and subscription edge cases. As of June 2026 it is free up to $2,500 in monthly tracked revenue, then 1% above that. This is the 30-minute setup path, the SwiftUI code, and an honest take on when raw StoreKit 2 is the better call.
TL;DR
- What it is: a hosted layer over Apple StoreKit that owns receipt validation, subscription state, cross-platform identity, and a server-driven paywall.
- Cost (June 2026): $0 up to $2,500/month in tracked revenue, then 1% of gross tracked revenue. Enterprise is custom-priced. (pricing)
- SDK:
purchases-ios5.x (5.76.0 as of June 3, 2026) uses StoreKit 2 by default on iOS 16+. Paywalls ship in theRevenueCatUIpackage. - Use it if you want IAP live this week and do not want to own a validation backend. Skip it if you have one app, simple non-consumables, and strong server engineering already.
RevenueCat vs. raw StoreKit 2
StoreKit 2 (Apple’s native framework) handles the purchase transaction and now does on-device receipt verification via signed Transaction objects. Everything around that — durable subscription state, cross-platform identity, analytics, and a no-code paywall — is still yours to build. RevenueCat hosts that layer.
| RevenueCat | Raw StoreKit 2 | |
|---|---|---|
| Receipt / transaction validation | Hosted, server-side | On-device Transaction.verify; your server if you need it elsewhere |
| Subscription state across devices/web | Built in (CustomerInfo) | Build it yourself |
| Cross-platform (iOS + Android + web) | One SDK, one user identity | Separate per platform |
| Paywall UI | Server-driven (RevenueCatUI), no app update | Hand-built SwiftUI |
| Webhooks (renewal, cancel, refund) | Built in | Apple App Store Server Notifications V2, you parse them |
| Cost | $0 to $2,500 MTR, then 1% | $0 (just your own infra time) |
| Lock-in | Migration takes days | None |
For most solo and small-team apps in 2026, RevenueCat’s 1% buys back days of subscription-plumbing work. The math flips when revenue is high enough that 1% of gross exceeds the cost of owning the infra, or when policy forbids sending purchase data to a third party.
Decide in 30 seconds
Use RevenueCat if most of these are true:
- You are adding auto-renewable subscriptions or non-consumables to an indie app.
- You do not want to run your own receipt-validation server.
- You expect to support multiple platforms eventually (iOS + Android + web).
- Tracked revenue is under $2,500/month, or the 1% above that is acceptable.
Skip it if you have strict no-third-party-data rules, very high revenue where 1% of gross is material, or a mature subscription backend already.
Setup, step by step
1. Create products in App Store Connect first
Product IDs are permanent — you cannot rename them — so pick a scheme you will not regret:
pro_monthly auto-renewable subscription $4.99/month
pro_yearly auto-renewable subscription $39.99/year
pro_lifetime non-consumable $79.99
Add a localized display name and description for each under App Store Connect → My Apps → Features → In-App Purchases (or Subscriptions). New subscriptions sit in “Missing Metadata” until you fill these in and attach a screenshot for review.
2. Connect App Store Connect to RevenueCat
In RevenueCat → Project Settings → API Keys, grab the iOS public SDK key (it starts with appl_). Then under Project Settings → Apple App Store, paste the App Store Connect API Issuer ID, Key ID, and the .p8 private key so RevenueCat can read your products and receive server notifications.
3. Configure Entitlements and Offerings
This is the indirection that lets you change pricing and packaging without shipping an app update:
Entitlement: "pro" (what access the user unlocks)
attached to
Products: pro_monthly, pro_yearly, pro_lifetime
Offering: "default" (what the paywall shows)
contains
Packages: $rc_monthly -> pro_monthly
$rc_annual -> pro_yearly
$rc_lifetime -> pro_lifetime
Gate features on the entitlement (pro), never on a product ID. Add a new price tier later and it just works.
4. Add both SDK packages via SPM
Add https://github.com/RevenueCat/purchases-ios and include two products: RevenueCat (core) and RevenueCatUI (paywalls). Initialize once at launch:
// App.swift
import RevenueCat
@main
struct MyApp: App {
init() {
Purchases.logLevel = .info
Purchases.configure(withAPIKey: "appl_XXXXXXXXXXXXXXXX")
}
var body: some Scene { WindowGroup { ContentView() } }
}
SDK 5.x targets StoreKit 2 by default on iOS 16+, so you get Apple’s signed-transaction verification and async/await without extra config.
5. Show the paywall (RevenueCatUI, not hand-rolled)
The fastest path is the presentPaywallIfNeeded modifier — it fetches the current offering, renders the dashboard-designed paywall, and only shows it when the entitlement is missing:
import SwiftUI
import RevenueCatUI
struct RootView: View {
var body: some View {
ContentView()
.presentPaywallIfNeeded(
requiredEntitlementIdentifier: "pro",
purchaseCompleted: { _ in /* unlocked */ },
restoreCompleted: { _ in /* dismissed if now active */ }
)
}
}
Need a manual trigger (a “Go Pro” button)? Present PaywallView() in a sheet:
.sheet(isPresented: $showPaywall) { PaywallView() }
Because the paywall is server-driven, you can A/B test layout and copy from the dashboard without resubmitting to review.
6. Gate features on the entitlement
func userHasPro() async -> Bool {
guard let info = try? await Purchases.shared.customerInfo() else { return false }
return info.entitlements["pro"]?.isActive == true
}
7. Restore Purchases (App Store Review Guideline 3.1.1 requires it)
Every app selling non-consumables or subscriptions must offer a Restore button, or it gets rejected. RevenueCat is one call:
func restore() async throws -> Bool {
let info = try await Purchases.shared.restorePurchases()
return info.entitlements["pro"]?.isActive == true
}
8. Verify server-side with webhooks
If you have a backend that grants access, do not trust the client alone. Listen to RevenueCat webhooks so cancellations, renewals, and refunds reach your server:
// Vercel / Cloudflare Worker — minimal webhook receiver
export async function POST(req: Request) {
const sig = req.headers.get('Authorization');
if (sig !== `Bearer ${process.env.RC_WEBHOOK_SECRET}`) {
return new Response('unauthorized', { status: 401 });
}
const event = await req.json();
// INITIAL_PURCHASE | RENEWAL | CANCELLATION | EXPIRATION | BILLING_ISSUE | ...
await updateUserEntitlement(event.event.app_user_id, event.event.type);
return new Response('ok');
}
9. Sandbox QA before submission
App Store Connect → Users and Access → Sandbox → Test Accounts → +
• create test@example.com (a new email, not your Apple ID)
• set Region = United States
Device → Settings → Developer → Sandbox Apple Account → sign in
TestFlight → install build → buy each package → tap Restore on a clean device
RevenueCat dashboard → Customer history shows each sandbox transaction within seconds; Apple’s own Sales reporting lags hours.
Don’t get caught by these
- Hard-coding product IDs in the app. Use Offerings so you can re-price and re-package without a release.
- No Restore button. Guideline 3.1.1 rejection. Wire up
restorePurchases(). - Ignoring the “pending purchase” state (Ask to Buy / parental approval, payment retries). Test it with sandbox Ask to Buy.
- Trusting client-side entitlement checks when you also gate server-side. Confirm state via webhooks for anything high-value.
- Charging a real card. Always create sandbox testers first; a real Apple Account buys for real.
- Treating the free tier as unlimited. Above $2,500/month tracked revenue you pay 1% — model it into your pricing.
- Forgetting Apple’s own cut. RevenueCat’s 1% is on top of Apple’s commission, not instead of it (see FAQ).
FAQ
- How much does RevenueCat cost? Free up to $2,500/month in tracked revenue, then 1% of gross tracked revenue. That fee is computed on revenue before Apple’s commission, so it sits on top of Apple’s 15% or 30% cut, not inside it. Enterprise is custom-priced.
- What does Apple itself take? 30% standard, but the App Store Small Business Program drops it to 15% for developers under $1M in annual proceeds (most indies), and standard auto-renewable subscriptions also fall to 15% after a subscriber’s first paid year.
- Does RevenueCat work with TestFlight? Yes. TestFlight builds run against the StoreKit sandbox and the SDK handles sandbox transactions correctly — no live charges.
- Do I still need StoreKit knowledge? A little. SDK 5.x uses StoreKit 2 under the hood, but you configure products, prices, and tax in App Store Connect yourself; RevenueCat does not replace that.
- What about Android? Same SDK and a unified API cover Google Play. One
app_user_idties a customer across iOS, Android, and web. - Can I migrate off RevenueCat later? Yes, but plan for several days. You can export transaction history and rebuild on raw StoreKit 2 / Google Play Billing, then re-validate entitlements.