---
name: ios-revenuecat-iap
description: "Subscriptions and IAP with RevenueCat — entitlements, paywalls, attribution, App Store + Play Store. Production RevenueCat setup: Offerings + Packages, paywall UI templates, entitlements, restore flows, App Store Server Notifications, refund handling, and the attribution patterns for Apple Search Ads and Meta Ads."
license: MIT
---

# iOS RevenueCat IAP

Production-grade in-app purchase and subscription setup for iOS apps using RevenueCat. This skill scaffolds the `purchases-ios` SDK, wires up Offerings, entitlements, and paywalls, and handles the operational edges most teams hit late: restore flows, server notifications, refunds, and ad-network attribution. It's aimed at iOS engineers (and the marketers who depend on their LTV numbers) who want a subscription stack that survives App Review and real-world edge cases.

## When to use it

- You're adding a paywall and don't want to hand-roll StoreKit 2 transaction handling.
- You need entitlement checks that work the same offline, after reinstall, and across devices.
- Sandbox purchases work but production restores, refunds, or renewals behave inconsistently.
- You're spending on Apple Search Ads or Meta and need install-to-subscription attribution that ties back to a `Purchases` app user ID.

## What it does

It configures the SDK with `Purchases.configure(withAPIKey:appUserID:)`, then sets up:

- **Offerings & Packages** — fetched via `Purchases.shared.offerings()`, reading `current.availablePackages` so pricing/products are controlled from the dashboard, not hard-coded.
- **Entitlements** — gating with `customerInfo.entitlements.active["pro"]?.isActive` and listening through the `PurchasesDelegate` / `customerInfoStream`.
- **Paywalls** — drops in RevenueCatUI's `PaywallView` and `.presentPaywallIfNeeded(requiredEntitlementIdentifier:)`, backed by remotely-configured templates.
- **Purchase & restore** — `purchase(package:)` and `restorePurchases()` with `userCancelled` handling.
- **Server side** — App Store Server Notifications V2 forwarded to RevenueCat, plus webhook handlers and refund handling via the Customer Info / REST API.
- **Attribution** — `Purchases.shared.attribution.setAttributes(...)`, `enableAdServicesAttributionTokenCollection()` for Apple Search Ads, and Meta via `setFBAnonymousID` / collectDeviceIdentifiers.

## Usage example

```swift
// AppDelegate / App init
Purchases.logLevel = .debug
Purchases.configure(withAPIKey: "appl_xxx")

// Show paywall only if not entitled
.presentPaywallIfNeeded(requiredEntitlementIdentifier: "pro")

// Manual purchase
let offerings = try await Purchases.shared.offerings()
if let pkg = offerings.current?.availablePackages.first {
    let result = try await Purchases.shared.purchase(package: pkg)
    if result.customerInfo.entitlements["pro"]?.isActive == true { unlock() }
}
```

## Tips & gotchas

- Check **entitlement identifiers**, never raw product IDs — products change, entitlements stay stable across plan migrations and platforms.
- Call `restorePurchases()` from an explicit button; App Review rejects paywalls without a visible restore path.
- Set the `appUserID` to your own stable user ID at login, or you'll fragment subscriber records across anonymous IDs. Use `logIn`/`logOut`, not reconfigure.
- Configure App Store Server Notifications V2 in App Store Connect pointing at RevenueCat — without it, renewal/refund state lags.

## FAQ

**Does this use StoreKit 2?** Yes — recent `purchases-ios` runs on StoreKit 2 by default with a StoreKit 1 fallback; you don't manage `Transaction` listeners yourself.

**Can I A/B test pricing?** Yes, through Offerings and dashboard Experiments — swap the `current` offering remotely without an app release.

**Refunds?** Apple refund decisions arrive via server notifications; the skill wires a webhook so entitlements revoke automatically.
