Guide · 8 min read
StoreKit 2 Migration for Indie Developers: What Changes, What Stays, and How to Handle Existing Subscribers
Apple deprecated the original StoreKit API at WWDC 2024, and every major subscription feature from WWDC 2026 — Group Purchases, Retention Messaging, cross-developer bundles — requires StoreKit 2. If you're still on the original API, those features are simply unavailable to you in App Store Connect. The good news: the migration is less disruptive than it looks. Existing subscribers carry over automatically, and StoreKit 2 works without a backend server, which removes the biggest objection most indie devs have to migrating.
"Deprecated" doesn't mean "broken" — what it actually means for your app today
Deprecated in Apple's terminology means "no new features will be added and we expect you to migrate" — not "this API stops working." Your existing StoreKit 1 app continues to process purchases on iOS 18 and later; existing transactions remain valid and the original queue-based API still works. Apple has not published an enforcement date that would fail App Store review for apps using it.
What changed is that all new IAP and subscription capabilities since WWDC 2024 are StoreKit 2-only. The original API is in maintenance mode: it receives bug fixes, nothing else. Every capability Apple has shipped in this space since 2024 — including the entire WWDC 2026 subscription overhaul — is exclusive to the new API.
The practical deadline is when you want access to new features, not a date Apple imposes. The pressure to migrate comes from the features you're locked out of: Group Purchases in winter 2026, Retention Messaging in fall 2026, and cross-developer bundles announced at WWDC 2026. If you don't need those capabilities yet, migrating is still a net code quality improvement — but it's not an emergency.
The 3 WWDC 2026 features locked behind StoreKit 2
Group Purchases, Retention Messaging, and cross-developer subscription bundles announced at WWDC 2026 are all built exclusively on StoreKit 2. Apps still using the original API cannot enable these features in App Store Connect — the configuration options are unavailable regardless of how the listing is set up.
Group Purchases let a single subscriber buy multiple subscription seats and invite others to join, with Apple handling the invite flow. For apps in education, family productivity, or team utilities, this is a meaningful monetization upgrade: a user who previously bought one seat can now bring in two or three more in a single transaction. How revenue attribution and refunds work is covered in the Group Purchases guide. Cross-developer subscription bundles go further — packaging your subscription alongside a non-competing app's subscription at a combined price, covered in the subscription bundles overview.
Retention Messaging is the most immediately valuable of the three for most apps. It lets you show a custom message or win-back offer to a subscriber at the exact moment they tap to cancel — the highest-intent moment in the subscription lifecycle. The basic version is a no-code configuration in App Store Connect, but the API integration (which supports dynamic content and targeted offers) requires StoreKit 2. The full setup is documented in the Retention Messaging tool guide.
The 4 core API replacements at the heart of the migration
The migration reduces to four concrete replacements: SKPaymentQueue → Transaction.updates, SKProductsRequest → Product.products(for:), receipt validation → on-device verified transactions, and SKPaymentTransactionObserver → an async/await Transaction listener. Each replacement maps directly to its predecessor in function — the architecture shift is from delegation callbacks to Swift concurrency.
SKPaymentQueue was the central coordinator in original StoreKit: you added observers, handled state machine transitions, and called finishTransaction manually. In StoreKit 2, Transaction.updates is an AsyncSequence that delivers verified transactions as they arrive. Start it at app launch inside a Task, iterate with for await transaction in Transaction.updates, update your entitlement state, and call transaction.finish(). No observer protocol, no queue management, no retain cycle risks from stored observer references.
Product.products(for:) replaces SKProductsRequest and is async/await by default — one line instead of a delegate-callback class. Verification switches from the opaque SKReceiptRefreshRequest flow to Transaction.currentEntitlements, which returns structured, signed transaction objects you can read directly. No shared secret to manage, no receipt blob to decode. Both improvements together reduce the IAP code surface in most apps by 40–60%.
Existing subscribers: Transaction.currentEntitlements picks them up automatically
On the first launch after a StoreKit 2 migration, Transaction.currentEntitlements returns all active subscription transactions — including purchases made through the original StoreKit API — with no special migration code required. Apple's purchase record is the source of truth and it's accessible through both API versions.
This is the largest practical concern for indie devs who have paying customers and are worried about disrupting them. The answer is that there's nothing to disrupt: a user who subscribed two years ago through your original StoreKit code appears in currentEntitlements on day one of your StoreKit 2 update, with a verified transaction that includes their subscription status, expiry date, and renewal information. You don't need to run a migration script, send an email, or handle any edge case for "old vs new" purchases.
The one edge case worth testing: users on promotional pricing or app-store-gifted subscriptions. These surface correctly in testing on a real device logged into a Sandbox account with the relevant plan type — verify them in Phase 1 of the migration before routing any live traffic. For apps weighing whether to stay on subscriptions versus restructure toward a different model, the in-app purchase vs subscription comparison documents the earnings and conversion tradeoffs.
Serverless verification: how on-device signing removes the backend requirement
Every StoreKit 2 transaction is signed by Apple using a JSON Web Signature (JWS) that your app verifies locally using Apple's public key — no network request to a server you control required. This is the architectural change that makes StoreKit 2 viable for solo developers without a dedicated backend.
Original StoreKit relied on a receipt file — an opaque binary blob you sent to Apple's receipt validation endpoint or your own server for decoding. That's why most indie devs ended up paying for RevenueCat or building a lightweight validation endpoint: reading receipt data directly on-device wasn't practical. StoreKit 2 replaces the receipt with structured, readable transaction objects embedded in a signed JWT. Product ID, purchase date, expiry date, transaction ID — all readable directly, all cryptographically verified by Apple's signature.
To go fully serverless: call Transaction.currentEntitlements at launch, iterate the results in an async for loop, and update local entitlement state. No shared secret in your codebase, no server endpoint, no third-party SDK required for basic subscription gating. Third-party platforms like RevenueCat still add value for analytics dashboards, cross-platform subscriber sync, and paywall A/B testing — but they're now optional rather than the only practical path to avoiding a server. While you're deploying this update, it's a good time to verify your App Store screenshot sizes and app icon dimensions are current for all device classes.
iOS 15 minimum deployment target — and how many users it actually costs you
StoreKit 2 requires iOS 15 as a minimum deployment target. As of mid-2026, Apple's own adoption statistics show iOS 14 and earlier on fewer than 1% of active iPhone and iPad devices, making this the smallest practical tradeoff in the migration.
The users below iOS 15 are predominantly on iPhone 6 and iPhone 7 hardware — devices no longer receiving software updates. In Apple's install data, this cohort consistently shows lower session frequency, lower purchase rates, and higher churn than users on current hardware. Raising the minimum to iOS 15 removes users who were already unlikely to convert to paid subscriptions.
There's a second benefit to the deployment target change beyond StoreKit compatibility: Swift concurrency (async/await, actors, structured concurrency) is fully available from iOS 15, which is what the entire StoreKit 2 API is built around. Keeping a target below 15 requires availability guards and conditional compilation paths that increase migration complexity significantly. If your app currently targets iOS 14, the StoreKit 2 migration is a natural forcing function to make the jump — it simplifies your concurrency model across the entire codebase, not just the payments layer.
Migration sequence: the order that minimizes subscriber disruption
Migrate in three phases: first run both APIs in parallel to verify parity, then move new purchases to StoreKit 2 while honoring existing SK1 receipts for renewals, then remove SK1 code after two full renewal cycles. This sequence ensures zero disruption for paying subscribers at every stage.
Phase 1 is read-only: integrate StoreKit 2 alongside your existing code without routing any purchases through it. Call Transaction.currentEntitlements, log the results, and compare against your existing entitlement state. This phase surfaces any edge cases — users on unusual plan types, promotional pricing, or family sharing — before they affect live traffic. Budget at least a week of logging across your active subscriber base before moving to Phase 2.
Phase 2 routes new purchases exclusively through StoreKit 2 while SK1 code continues to validate renewals for existing subscribers. After one full renewal cycle (typically 30 days for monthly subscribers), you'll have confirmation that renewals surface correctly in currentEntitlements and your entitlement logic handles both code paths cleanly. Phase 3 is cleanup: remove SK1 code, remove the shared secret from your build configuration (no longer needed for on-device verification), and ship as a minor version bump. The two-cycle wait rather than one accounts for annual subscribers whose next renewal hasn't fired during a monthly observation window.
The migration is smaller than the feature table makes it look
The SKPaymentQueue-to-Transaction.updates swap is the core of the work; everything else follows from it. Most indie devs shipping apps under 50K MAU complete the full migration in a single sprint, with Phase 1 taking the longest — not because of code volume, but because of waiting to see a statistically meaningful slice of existing subscribers surface correctly in the new API before moving live traffic.
The payoff beyond just having done it: Group Purchases, Retention Messaging, and cross-developer bundles are now available for you to enable in App Store Connect at any point. The basic versions require no additional coding once StoreKit 2 is in place. Ship the migration, then turn the features on.
Update your App Store listing in the screenshot editor →
Frequently asked questions
will original storekit apps stop working in 2026?
No. Apple deprecated the original StoreKit API but has not published an enforcement date that would break existing apps or fail App Store review for apps using it. Deprecation means Apple has stopped adding features to the original API — not that it is removed or that existing purchases stop processing. Apps on the original StoreKit continue to function on iOS 18 and later.
do i need a server to use storekit 2?
No. StoreKit 2 is designed to work entirely serverless for basic subscription management. Every transaction is cryptographically signed by Apple and verifiable on-device using the Transaction type, with no network request to a server you control. A server is only needed for advanced use cases: historical transaction analytics via the App Store Server API, cross-device sync, or real-time server-to-server subscription status notifications.
what happens to existing subscribers when i migrate to storekit 2?
Nothing changes from the subscriber's perspective. On first launch after your migration, Transaction.currentEntitlements returns all active subscriptions — including purchases made through the original StoreKit — as verified StoreKit 2 transaction objects. No migration script, no subscriber communication, no special handling required. Apple's purchase record is the source of truth and it's accessible through both API versions.
does storekit 2 work with non-consumable in-app purchases or only subscriptions?
StoreKit 2 supports all three IAP types: auto-renewable subscriptions, non-consumable purchases (one-time unlocks), and consumable purchases (in-game currency or credits). The same Transaction.currentEntitlements and Product.products(for:) APIs cover all three. Non-consumables appear in currentEntitlements for the lifetime of the entitlement; consumables arrive as transactions in your Transaction.updates listener and should be handled and finished there.
what ios version does storekit 2 require?
StoreKit 2 requires iOS 15, iPadOS 15, macOS 12, watchOS 8, and tvOS 15 as minimum deployment targets. As of mid-2026, Apple's adoption data shows iOS 14 and earlier on fewer than 1% of active devices, making this requirement a minimal practical constraint for the vast majority of apps.