
Storekit
Implement or fix StoreKit 2 subscriptions, paywalls, entitlements, and purchase flows on iOS with modern Product and Transaction APIs.
Install
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill storekitWhat is this skill?
- StoreKit 2 coverage: Product, Transaction, SubscriptionStoreView, StoreView on iOS 17+
- Purchase flows for consumable, non-consumable, and auto-renewable products with Transaction.updates listener
- Entitlement verification, subscription status, renewal state, restore, and App Transaction checks
- Promotional, introductory, win-back, and offer codes plus Family Sharing and Ask to Buy notes
- Explicit guidance to avoid legacy SKProduct, SKPaymentQueue, and SKStoreReviewController APIs
Adoption & trust: 1.8k installs on skills.sh; 713 GitHub stars; 2/3 security scanners passed (skills.sh audits).
Recommended Skills
Vercel React Native Skillsvercel-labs/agent-skills
Firebase Basicsfirebase/agent-skills
Building Native Uiexpo/skills
Firebase Ai Logic Basicsfirebase/agent-skills
Native Data Fetchingexpo/skills
Firebase Firestorefirebase/agent-skills
Journey fit
Primary fit
Canonical shelf is Build → frontend because paywalls, SubscriptionStoreView, and purchase UI are the primary implementation surface for solo iOS apps. Mobile client purchase UX and StoreKit views live in the iOS frontend layer even when backed by server receipt logic.
Common Questions / FAQ
Is Storekit safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Storekit
# StoreKit 2 In-App Purchases and Subscriptions Implement in-app purchases, subscriptions, and paywalls using StoreKit 2 on iOS 26+. Use the modern `Product`, `Transaction`, `StoreView`, and `SubscriptionStoreView` APIs. Avoid the older original StoreKit APIs (`SKProduct`, `SKPaymentQueue`, `SKStoreReviewController`). ## Contents - [Product Types](#product-types) - [Loading Products](#loading-products) - [Purchase Flow](#purchase-flow) - [Transaction.updates Listener](#transactionupdates-listener) - [Entitlement Checking](#entitlement-checking) - [SubscriptionStoreView (iOS 17+)](#subscriptionstoreview-ios-17) - [StoreView (iOS 17+)](#storeview-ios-17) - [Subscription Status Checking](#subscription-status-checking) - [Restore Purchases](#restore-purchases) - [App Transaction (App Purchase Verification)](#app-transaction-app-purchase-verification) - [Purchase Options](#purchase-options) - [SwiftUI Purchase Callbacks](#swiftui-purchase-callbacks) - [Common Mistakes](#common-mistakes) - [Review Checklist](#review-checklist) - [References](#references) ## Product Types | Type | Enum Case | Behavior | |---|---|---| | **Consumable** | `.consumable` | Used once, can be repurchased (gems, coins) | | **Non-consumable** | `.nonConsumable` | Purchased once permanently (premium unlock) | | **Auto-renewable** | `.autoRenewable` | Recurring billing with automatic renewal | | **Non-renewing** | `.nonRenewing` | Time-limited access without automatic renewal | ## Loading Products Define product IDs as constants. Fetch products with `Product.products(for:)`. ```swift import StoreKit enum ProductID { static let premium = "com.myapp.premium" static let gems100 = "com.myapp.gems100" static let monthlyPlan = "com.myapp.monthly" static let yearlyPlan = "com.myapp.yearly" static let all: [String] = [premium, gems100, monthlyPlan, yearlyPlan] } let products = try await Product.products(for: ProductID.all) for product in products { print("\(product.displayName): \(product.displayPrice)") } ``` ## Purchase Flow Call `product.purchase(options:)` and handle all three `PurchaseResult` cases. Always verify and finish transactions. ```swift func purchase(_ product: Product) async throws { let result = try await product.purchase(options: [ .appAccountToken(userAccountToken) ]) switch result { case .success(let verification): let transaction = try checkVerified(verification) await deliverContent(for: transaction) await transaction.finish() case .userCancelled: break case .pending: // Ask to Buy or deferred approval -- do not unlock content yet break @unknown default: break } } func checkVerified<T>(_ result: VerificationResult<T>) throws -> T { switch result { case .verified(let value): return value case .unverified(_, let error): throw error } } ``` ## Transaction.updates Listener Start at app launch. Catches purchases from other devices, Family Sharing changes, renewals, Ask to Buy approvals, refunds, and revocations. ```swift @main struct MyApp: App { private var transactionListener: Task<Void, Error>? init() { transactionListener = listenForTransactions() } var body: some Scene { WindowGroup { ContentView() } } func listenForTransactions() -> Task<Void, Error> { Tas