Agent Skills: StoreKit

Use when implementing in-app purchases, StoreKit 2 subscriptions, consumables, non-consumables, or transaction handling. Covers testing-first workflow with .storekit configuration, StoreManager architecture, and transaction verification.

UncategorizedID: johnrogers/claude-swift-engineering/storekit

Install this agent skill to your local

pnpm dlx add-skill https://github.com/johnrogers/claude-swift-engineering/tree/HEAD/plugins/swift-engineering/skills/storekit

Skill Files

Browse the full folder contents for storekit.

Download Skill

Loading file tree…

plugins/swift-engineering/skills/storekit/SKILL.md

Skill Metadata

Name
storekit
Description
Use when implementing in-app purchases, StoreKit 2 subscriptions, consumables, non-consumables, or transaction handling. Covers testing-first workflow with .storekit configuration, StoreManager architecture, and transaction verification.

StoreKit

StoreKit 2 patterns for implementing in-app purchases with async/await APIs, automatic verification, and SwiftUI integration.

Reference Loading Guide

ALWAYS load reference files if there is even a small chance the content may be required. It's better to have the context than to miss a pattern or make a mistake.

| Reference | Load When | |-----------|-----------| | Getting Started | Setting up .storekit configuration file, testing-first workflow | | Products | Loading products, product types, purchasing with Product.purchase() | | Subscriptions | Auto-renewable subscriptions, subscription groups, offers, renewal tracking | | Transactions | Transaction listener, verification, finishing transactions, restore purchases | | StoreKit Views | ProductView, SubscriptionStoreView, SubscriptionOfferView in SwiftUI |

Core Workflow

  1. Create .storekit configuration file first (before any code)
  2. Test purchases locally in Xcode simulator
  3. Implement centralized StoreManager with @MainActor
  4. Set up Transaction.updates listener at app launch
  5. Display products with ProductView or custom UI
  6. Always call transaction.finish() after granting entitlements

Essential Architecture

@MainActor
final class StoreManager: ObservableObject {
    @Published private(set) var products: [Product] = []
    @Published private(set) var purchasedProductIDs: Set<String> = []
    private var transactionListener: Task<Void, Never>?

    init() {
        transactionListener = listenForTransactions()
        Task { await loadProducts() }
    }
}

Common Mistakes

  1. Missing .finish() calls on transactions — Forgetting to call transaction.finish() after granting entitlements causes transactions to never complete. The user won't see their purchase reflected. Always call finish().

  2. Unsafe StoreManager state — Shared StoreManager without @MainActor can have race conditions. Multiple async tasks can update @Published properties concurrently, corrupting state. Use @MainActor for thread safety.

  3. No transaction listener at app launch — Not setting up Transaction.updates listener means app crashes or misses refunded/canceled purchases. Listen for transactions immediately in @main, not when user taps purchase button.

  4. Hardcoded product IDs — Hardcoded IDs make testing and localization hard. Use configuration files or environment variables for product IDs. Same applies to prices (fetch from App Store, don't hardcode).

  5. Ignoring verification failures — App Store verification fails silently sometimes. Not checking verification status means accepting unverified transactions (security risk). Always verify before granting entitlements.