In-app purchases with StoreKit 2

Modern platform Mid-level Swift 5.5+ iOS 17+ Reviewed By Alok Singh

Spot the bug

Tap the line that makes this purchase flow unsafe.

  1. func buy(_ product: Product) async throws {
  2. let result = try await product.purchase()
  3. switch result {
  4. case .success(let verification):
  5. let transaction = verification.unsafePayloadValue
  6. unlockPremium(for: transaction.productID)
  7. await transaction.finish()
  8. case .userCancelled, .pending:
  9. break
  10. @unknown default:
  11. break
  12. }
  13. }
Hint 1 of 1

What does 'unsafe' skip?

Choose an answer, then open Show answer below to check it.

Show answer
  • Answer: line 5 let transaction = verification.unsafePayloadValue

Line 5 skips verification. Switch on the result and unlock only for .verified(let transaction), or use try verification.payloadValue, which throws when unverified.

30-second answer

Load products with Product.products(for:), call product.purchase(), and check the VerificationResult: only unlock for .verified transactions, then call transaction.finish(). Listen to Transaction.updates from launch to catch renewals, purchases on other devices and Ask to Buy approvals, and compute access from Transaction.currentEntitlements. SwiftUI's StoreView and SubscriptionStoreView give ready-made store UI.

Practise saying it · 45 s. No recording, just a timer

Explain a StoreKit 2 purchase flow.

Say your answer out loud, then check which of these points you covered:

  • Products and purchase
  • Verify then finish
  • Transaction.updates
  • Current entitlements

Deep dive

Test with a StoreKit configuration file and Xcode's transaction manager.

AppStore.sync() is the restore button, used only on request.

Pass appAccountToken to link purchases to your user account.

Use App Store Server API and notifications for server-side truth.

Common mistakes

  • Unlocking unverified transactions.
  • Not listening to Transaction.updates.

Senior take & follow-ups

Seniors verify, finish, listen for updates and derive access from entitlements.

Follow-up questions

Think of your answer first, then open the hint.

  1. Why listen to Transaction.updates at launch?

    Show hint

    Transactions can complete outside the purchase call.

  2. What does finish() do?

    Show hint

    Tells StoreKit you've delivered the content.

Suggest a fix if something here is wrong or out of date.