Swift and StoreKit 2
Integrate the Apple SDK, submit verified purchases, and finish safely.
Download this chapterOn this page
The Swift package is AppVisionIAP in sdk/swift. It supports iOS 15, macOS 12, tvOS 15, watchOS 8, and visionOS 1 or later, and its manifest uses Swift tools 6.0. Public registry/package distribution is not finalized in this preview; add the local package from your approved CherrySub source checkout.
Add the package and configure authentication
In Xcode, add sdk/swift as a local Swift package dependency and link the AppVisionIAP product. Prepare your authenticated token broker and the app's publishable key.
In these examples, sessionAPI is your application's authenticated networking client and user.id is your stable App User ID. They are not CherrySub SDK symbols.
import AppVisionIAP
import StoreKit
let tokens = try InMemoryCustomerTokenProvider { appUserID in
let response = try await sessionAPI.createIAPToken(for: appUserID)
return CustomerToken(token: response.token, expiresAt: response.expiresAt)
}
let iap = try IAPClient(
baseURL: URL(string: "https://api-staging.cherrysub.com")!,
publishableKey: "iap_pk_REPLACE_WITH_YOUR_KEY",
customerTokenProvider: tokens.provider
)Your broker chooses the user from its verified application session and returns token plus expiry. Do not embed iap_sk_ or store credentials in the app target.
Load the current product set
let productSet = try await iap.currentProductSet(appUserID: user.id)A nil set means none is selected. Use its ordered Apple packages to load StoreKit products, then show StoreKit's localized price and purchase options. Your app owns the purchase UI and unavailable-product state.
Submit and finish a purchase
if case .success(let verification) = try await product.purchase() {
try await iap.submitAndFinishAppleTransaction(
appUserID: user.id,
verificationResult: verification
)
}
let subscriber = try await iap.subscriber(appUserID: user.id)
let hasPro = subscriber.accessLevels["pro"]?.active == trueHandle StoreKit's pending and user-cancelled outcomes separately in your UI. The helper refuses an unverified transaction and only calls finish() after backend acceptance. If backend verification fails, retain the unfinished transaction for a safe retry.
The submit-only StoreKit overload is available when your application owns completion. A raw transactionJWS overload supports a custom bridge, but it still requires the actual signed StoreKit transaction JWS.
Recover unfinished purchases and restore
Connect the storefront's transaction-update and restore paths to the same authenticated submission policy. A purchase can complete while the app is interrupted; handling only the immediate product.purchase() result is insufficient for recovery.
Use the intended application account for restore. A verified restore can transfer that purchase's ownership without merging all customer identities. Ownership policy.
Token lifecycle and errors
The in-memory provider caches per user, refreshes 30 seconds before expiry by default, and coalesces simultaneous refreshes. A 401 triggers one forced refresh and retry. Other failures need your application's retry/error policy.
On logout:
try await tokens.invalidate(appUserID: user.id)Also clear your application-owned access state and revoke the server-side customer token if required. A subscriber 404 before the first receipt is not active access. Preserve ledger event IDs as strings.
Validate the integration
Run the package's swift test from sdk/swift when changing SDK integration code. The repository's iOS sandbox app demonstrates product-set-driven purchase configuration. Verify a store-backed sandbox purchase, duplicate retry, restore, cancellation/expiry, refund, and account switch using Testing. Local StoreKit fixtures alone do not prove Apple server verification.