StoreKit 2 In-App Purchases and Subscriptions
Implement in-app purchases, subscriptions, paywalls, and StoreKit testing using
StoreKit 2. Use the modern Swift-based Product, Transaction,
PurchaseAction, StoreView, and SubscriptionStoreView APIs. Avoid original
In-App Purchase APIs (SKProduct, SKPaymentQueue) unless legacy OS support
requires them.
StoreKit views initiate purchases automatically. For custom controls, use
PurchaseAction in SwiftUI, purchase(confirmIn:options:) in UIKit/AppKit, and
product.purchase(options:) on watchOS.
Contents
- Product Types
- Loading Products
- Purchase Flow
- Transaction.updates Listener
- Entitlement Checking
- SubscriptionStoreView (iOS 17+)
- StoreView (iOS 17+)
- Subscription Status Checking
- Restore Purchases
- App Transaction (App Purchase Verification)
- Purchase Options
- SwiftUI Purchase Callbacks
- Common Mistakes
- Review Checklist
- References
Product Types
Loading Products
Define product IDs as constants. Fetch products with Product.products(for:).
Purchase Flow
Prefer StoreKit views for standard paywalls because they initiate purchases,
restore purchases, and display policy controls. For custom SwiftUI purchase
buttons, prefer PurchaseAction from the environment. Use direct
product.purchase(options:) for watchOS, and use purchase(confirmIn:options:)
for UIKit or AppKit confirmation. Always handle every PurchaseResult, verify
before access, deliver durably, then finish.
Transaction.updates Listener
Start at app launch, not when a paywall appears. Catches purchases from other devices, Family Sharing changes, renewals, Ask to Buy approvals, refunds, revocations, and unfinished transactions Apple emits once immediately after launch. Keep the task retained for the app lifetime.
Entitlement Checking
Transaction.currentEntitlements emits non-consumables, active or grace-period
auto-renewable subscriptions, and the latest non-renewing subscription
transaction—including finished ones. It excludes consumables and refunded or
revoked products. Track consumable fulfillment separately, and apply the app's
expiration policy to non-renewing subscriptions before granting access.
SwiftUI .currentEntitlementTask Modifier
SubscriptionStoreView (iOS 17+)
Built-in SwiftUI view for subscription paywalls. Handles product loading, purchase UI, and restore purchases automatically.
Custom Marketing Content
Use the container background and header patterns in SubscriptionStoreView Control Styles [blocked].
Hierarchical Layout
Use SubscriptionOptionGroup, SubscriptionOptionSection, or
SubscriptionPeriodGroupSet to organize iOS 18+ options; see
Subscription Group Management [blocked].
StoreView (iOS 17+)
Merchandises multiple products with localized names, prices, and purchase buttons.
ProductView for Individual Products
Subscription Status Checking
Renewal States
Restore Purchases
StoreKit 2 handles restoration via Transaction.currentEntitlements. Add a
restore button or call AppStore.sync() explicitly.
On store views: .storeButton(.visible, for: .restorePurchases)
App Transaction (App Purchase Verification)
Verify the legitimacy of the app installation. Use for business model changes or detecting tampered installations (iOS 16+).
Purchase Options
SwiftUI Purchase Callbacks
Common Mistakes
1. Not starting Transaction.updates at app launch
2. Forgetting transaction.finish()
3. Ignoring verification result
4. Using original In-App Purchase APIs in new StoreKit 2 code
5. Not checking revocationDate
6. Hardcoding prices
7. Not handling .pending purchase result
8. Checking entitlements only once at launch
9. Missing restore purchases button
10. Subscription views without policy links
Review Checklist
-
Transaction.updateslistener starts at app launch in App init - All transactions verified before granting access
-
transaction.finish()called only after durable content delivery - Revoked/refunded transactions excluded and entitlement state updated
-
.pendingresult shows Ask to Buy/deferred-approval feedback - Restore purchases button visible on paywall and store views
- Terms of Service and Privacy Policy links on subscription views
- Prices shown using
product.displayPrice, never hardcoded - Subscription terms (price, duration, renewal) clearly displayed
- Free trial states post-trial pricing clearly
- No original In-App Purchase APIs (
SKProduct,SKPaymentQueue) unless legacy OS support requires them - Product IDs defined as constants, not scattered strings
- StoreKit tests cover promotional offers, win-back, offer codes, Ask to Buy, renewals, refunds, and revocations
- Entitlements re-checked on Transaction.updates and app foreground
- Server-side validation uses
jwsRepresentationif applicable - Consumables delivered and finished promptly
- Transaction observer types and product model types are
Sendablewhen shared across concurrency boundaries
References
- See references/app-review-guidelines.md [blocked] for IAP rules (Guideline 3.1.1), subscription display requirements, and rejection prevention.
- See references/storekit-advanced.md [blocked] for subscription control styles, offer management, testing patterns, and advanced subscription handling.
- For submission, privacy, metadata, screenshots, and rejection-risk audits use
app-store-review. - For keyword, screenshot-caption, ranking, and conversion strategy use
app-store-optimization. - Official Apple docs: Choosing a StoreKit API, Transaction.updates, Transaction.currentEntitlements, SubscriptionStoreView, and PurchaseAction.


