In-App Purchases (StoreKit)
In-App Purchases (StoreKit)
Purchase in-app products with StoreKit 2 by requesting products, purchasing, and verifying transactions.
Product Types
- Consumable: Can be bought multiple times (e.g., coins).
- Non-Consumable: One-time unlock (e.g., premium upgrade).
- Auto-Renewable Subscription: Recurring access.
StoreKit 2 Basics
Use StoreKit 2 to request products, purchase, and verify transactions with App Store-signed receipts.
Syntax: let products = try await Product.products(for:), try await product.purchase(), verify and transaction.finish().
Example
import StoreKit
@MainActor
func buy(productID: String) async throws {
let products = try await Product.products(for: [productID])
guard let product = products.first else { return }
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try verification.payloadValue
// unlock content
await transaction.finish()
default: break
}
}
This example requests a product, performs a purchase, verifies the transaction, unlocks content, and finishes the transaction.
Fetch Products
Fetch products once (e.g., on app start) and keep them in memory for your paywall/store UI.
Example
import StoreKit
@MainActor
func loadProducts(ids: [String]) async throws -> [Product] {
let products = try await Product.products(for: ids)
return products.sorted { $0.displayName < $1.displayName }
}
Purchase Flow & Results
Handle all outcomes and always verify before unlocking content.
Example
import StoreKit
@MainActor
func purchase(_ product: Product) async {
do {
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try verification.payloadValue
// unlock content guarded by transaction.productID
await transaction.finish()
case .userCancelled:
// show nothing or restore UI state
break
case .pending:
// family approval or SCA; update UI accordingly
break
@unknown default:
break
}
} catch {
// network or App Store errors
}
}
Listen for Transaction Updates
Receive purchases made outside your app UI (e.g., from the App Store or another device) and finish them.
Example
import StoreKit
func startTransactionListener() {
Task.detached {
for await update in Transaction.updates {
do {
let transaction = try update.payloadValue
// update entitlements based on transaction.productID
await transaction.finish()
} catch {
// handle verification failure
}
}
}
}
Restore Purchases
Query current entitlements to restore non-consumables and active subscriptions.
Example
import StoreKit
@MainActor
func restore() async {
for await result in Transaction.currentEntitlements {
if let transaction = try? result.payloadValue {
// re-unlock features for transaction.productID
}
}
}
Tip: Configure IAPs in App Store Connect and test with Sandbox accounts.
Use server-side receipt validation for security-sensitive unlocks.
Testing & Sandbox
Use TestFlight or the StoreKit Configuration file to simulate products; sign in with a Sandbox tester on device; note that sandbox transactions settle faster, and device logs help diagnose failures.