Blog ยท iOS Development
๐Ÿ“ฑ iOS Development

StoreKit 2 In-App Purchase: The Modern Way

StoreKit 2 was Apple's complete rewrite of the IAP API, shipping with iOS 15. It's async/await-native, dramatically simpler than the original StoreKit, and the only reasonable choice for new apps in 2026. This is the working pattern we use in the Aether AI apps.

Why StoreKit 2

Setup in App Store Connect

  1. Sign Paid Apps agreement
  2. Configure banking and tax info
  3. Create the IAP in App Store Connect โ€” Product ID like com.djenterprises.rdr2.unlimited_ai
  4. For testing: create a Sandbox tester account

Fetching products

import StoreKit

let productIDs: Set<String> = ["com.djenterprises.rdr2.unlimited_ai"]
let products = try await Product.products(for: productIDs)
let unlock = products.first { $0.id == productIDs.first }

Making a purchase

do {
    let result = try await unlock.purchase()
    switch result {
    case .success(let verification):
        let transaction = try checkVerified(verification)
        // Grant entitlement
        await transaction.finish()
    case .userCancelled:
        break
    case .pending:
        // Awaiting parental approval, etc.
        break
    @unknown default:
        break
    }
} catch {
    // Handle error
}

The checkVerified helper extracts the transaction and checks the JWS signature. Apple has a recommended pattern in their sample code.

Transaction listener

On app launch, listen for transactions that arrived while the app was closed (refunds, family sharing, etc.):

Task {
    for await result in Transaction.updates {
        guard case .verified(let txn) = result else { continue }
        // Update entitlement state
        await txn.finish()
    }
}

Restore purchases

// In your restore button handler:
try await AppStore.sync()

Then re-check Transaction.currentEntitlements to see what the user owns.

Testing with StoreKit configuration

Xcode's StoreKit Configuration file lets you simulate IAPs locally โ€” no App Store Connect round-trip required. File โ†’ New โ†’ StoreKit Configuration, define products, then in your scheme: Run โ†’ Options โ†’ StoreKit Configuration. Purchases work in the simulator.

Test these scenarios before submission:

Server-side receipt validation

For most consumer apps, on-device verification via checkVerified is sufficient. For premium / subscription apps where revenue matters more, validate on your backend using Apple's App Store Server API.

The pattern: when the user makes a purchase, send the JWS to your backend, your backend verifies with Apple's API and grants the entitlement in your database. This prevents jailbreak/MITM bypass at the cost of complexity.


For the full iOS app development story including how this fits with Claude API integration, see Shipping the RDR2 Companion From Scratch.

Sources & References
  1. Apple โ€” StoreKit documentation
  2. Apple โ€” WWDC21: Meet StoreKit 2
  3. Apple โ€” App Store Server API
  4. RevenueCat โ€” Subscription IAP best practices