App Store - StoreKit 1 vs StoreKit 2

🧭 Overview

Apple introduced StoreKit 2 at WWDC 2021 as a modern, Swift-first replacement for the legacy StoreKit 1 framework.
While StoreKit 1 powered in-app purchases for over a decade, it has now been officially deprecated and will no longer receive updates or support for new features.

StoreKit 2 simplifies in-app purchase logic, improves reliability, enhances security, and offers native Swift concurrency and built-in paywall UI components.

👍

Purchasely handles it automatically

Purchasely automatically uses StoreKit 2, with a seamless fallback to StoreKit 1 on devices running iOS 14 or earlier.
You can still force StoreKit 1 if needed, but this is not recommended and should only be used temporarily while migrating your own codebase to StoreKit 2.

We recommend using the Purchasely SDK in full mode and letting Purchasely manage all StoreKit transactions for you.
This way, you benefit from the latest Apple frameworks, zero configuration, and a fully reliable purchase flow out of the box.


⚠️ Deprecation and Lifecycle

AspectStoreKit 1StoreKit 2
StatusDeprecated as of WWDC 2024Actively developed
OS supportiOS 3 → iOS 18 (legacy)iOS 15 +
New features❌ None✅ Continuous updates
Future supportMaintenance only, may break in future iOS versionsFull support for new features, APIs, and server integrations
🔹

Apple announced the deprecation of “Original StoreKit API” at WWDC 2024.

Developers are strongly encouraged to migrate to StoreKit 2 as StoreKit 1 behaviour may change or stop functioning in future OS versions.


🧩 Feature Comparison

CategoryStoreKit 1StoreKit 2
LanguageObjective-C and (early) Swift; delegate-based (SKPaymentQueue, SKProductsRequest)Swift-only; async/await concurrency, strongly typed APIs
Product retrievalSKProductsRequest with delegate callbacksProduct.products(for:) (async)
Purchase / transaction handlingAdd SKPayment to queue; observer pattern with delegate callbacks handles updates manuallytry await product.purchase() returns structured results via async/await
Restore purchases / flowManual "Restore" button; requires calling restoreCompletedTransactions(), complex logicTransaction.currentEntitlements provides real-time entitlements automatically; automatic sync across devices
EntitlementsMust be persisted manually and restored explicitlyManaged automatically via Transaction.currentEntitlements
Subscription status / renewal infoRequires server validation and custom logicBuilt-in SubscriptionStatus and Transaction API, plus RenewalInfo
Receipt validationManual receipt parsing (ASN.1 / JSON); app receipt must be read and verified manuallyBuilt-in cryptographic verification (VerificationResult, JWS signed transactions)
SecurityDeveloper-managedTransactions cryptographically signed by Apple (JWS)
UI / Paywall integrationFully customBuilt-in SwiftUI views: StoreView, ProductView, SubscriptionStoreView, etc.
TestingSandbox only; limited StoreKit configuration supportEnhanced StoreKit Testing in Xcode with full simulation (renewals, refunds, billing retry)
Server integration / communicationApp Store Server API v1 (deprecated)App Store Server API v2 with richer transaction data (modern JWS model)
Cross-device syncManual restore logicAutomatic entitlement syncing across devices

🔐 Security Enhancements

StoreKit 2 introduces end-to-end cryptographic verification:

  • All transactions are signed by Apple using JSON Web Signature (JWS).
  • The SDK validates signatures locally, reducing the need for complex receipt parsing.
  • Built-in transaction verification ensures authenticity and integrity.
  • Better protection against tampering or fraudulent unlocks.
  • Simplified server-side validation through App Store Server API v2.

💡 Implementation Overview

StoreKit 1 (Legacy Example)

let payment = SKPayment(product: product)  
SKPaymentQueue.default().add(payment)

func paymentQueue(\_ queue: SKPaymentQueue,  
                  updatedTransactions transactions: [SKPaymentTransaction]) {  
    for transaction in transactions {  
        switch transaction.transactionState {  
        case .purchased:  
            // Unlock content  
            SKPaymentQueue.default().finishTransaction(transaction)  
        case .restored:  
            // Restore purchases  
            SKPaymentQueue.default().finishTransaction(transaction)  
        case .failed:  
            // Handle error  
            SKPaymentQueue.default().finishTransaction(transaction)  
        default:  
            break  
        }  
    }  
}

StoreKit 2 (Modern Example)

import StoreKit

let products = try await Product.products(for: ["com.myapp.pro_monthly"])  
if let product = products.first {  
    let result = try await product.purchase()  
    switch result {  
    case .success(let verification):  
        if case .verified(let transaction) = verification {  
            // Unlock content  
            await transaction.finish()  
        } else {  
            // Handle unverified transaction  
        }  
    case .pending:  
        // Ask to Buy / SCA  
        break  
    case .userCancelled:  
        break  
    @unknown default:  
        break  
    }  
}

Checking Entitlements (No Restore Needed)

for await transaction in Transaction.currentEntitlements {  
    // Automatically includes all active purchases  
    print("User owns: \(transaction.productID)")  
}

⚙️ Testing Improvements

StoreKit 2 introduces a more robust and developer-friendly testing environment:

  • Xcode StoreKit Testing Configuration allows local simulation of subscriptions, renewals, refunds, and billing issues.
  • Async purchase simulation enables easy testing of success, pending, and cancellation flows.
  • Unified transaction model ensures identical behaviour on simulator and real devices.
  • Automatic entitlement refresh prevents stale purchase states during tests.

🚧 Restrictions & Compatibility

LimitationDescription
Minimum OS versionStoreKit 2 requires iOS 15+, macOS 12+, tvOS 15+, or watchOS 8+
Backward compatibilityFor users on iOS 14 or older, StoreKit 1 fallback may be required
Promoted in-app purchasesSome legacy App Store flows remain StoreKit 1-only
Objective-C projectsStoreKit 2 is Swift-native — migration or bridging needed
Server integrationRequires App Store Server API v2 adoption
Migration costCode refactor required for purchase logic and validation


🧭 Recommendations

👍

Use Purchasely SDK in full mode

To simplify entitlement management, paywall configuration, and cross-platform consistency.

  • For new apps: adopt StoreKit 2 only.
  • For existing apps: implement a hybrid setup during migration.
  • Avoid building new logic on StoreKit 1 — it’s deprecated and will eventually stop functioning.
  • Update your backend for App Store Server API v2 and JWS verification.
  • Test critical edge cases: refunds, family sharing, Ask to Buy, and billing retry.

📚 References


Did this page help you?