All skills

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.

Use this Skill: https://skilld.dev/gh/johnrogers/claude-swift-engineering/storekit

This session only. Nothing lands on disk.

referencestransactions.md

≈924 tokens on demand. Your agent reads this file only when SKILL.md points to it.

Transactions

Transaction handling, verification, and restore purchases.

Transaction Listener (REQUIRED)

Set up at app launch to catch all transaction sources:

func listenForTransactions() -> Task<Void, Never> {
    Task.detached { [weak self] in
        for await verificationResult in Transaction.updates {
            await self?.handleTransaction(verificationResult)
        }
    }
}

Transaction sources: In-app purchases, App Store purchases, offer codes, renewals, Family Sharing, Ask to Buy completions, refunds.

Transaction Verification

Always verify before granting entitlements:

private func handleTransaction(_ result: VerificationResult<Transaction>) async {
    switch result {
    case .verified(let transaction):
        await grantEntitlement(for: transaction)
        await transaction.finish()

    case .unverified(let transaction, let error):
        print("Unverified: \(error)")
        await transaction.finish()  // Still finish to clear queue
    }
}

Transaction Properties

// Basic
transaction.id, transaction.originalID, transaction.productID
transaction.productType, transaction.purchaseDate, transaction.appAccountToken

// Subscription
transaction.expirationDate, transaction.isUpgraded
transaction.revocationDate, transaction.revocationReason

// Offer (iOS 18.4+)
transaction.offer?.type, transaction.offer?.id, transaction.offer?.paymentMode

Grant Entitlements

func grantEntitlement(for transaction: Transaction) async {
    guard transaction.revocationDate == nil else {
        await revokeEntitlement(for: transaction.productID)
        return
    }

    switch transaction.productType {
    case .consumable:     await addConsumable(productID: transaction.productID)
    case .nonConsumable:  await unlockFeature(productID: transaction.productID)
    case .autoRenewable:  await activateSubscription(productID: transaction.productID)
    default: break
    }
}

Finishing Transactions (CRITICAL)

await transaction.finish()

When to finish: After granting entitlement, after storing receipt, even for unverified/refunded transactions.

If you don't finish: Transaction redelivered on next app launch, queue builds up.

Current Entitlements

for await result in Transaction.currentEntitlements {
    guard let transaction = try? result.payloadValue,
          transaction.revocationDate == nil else { continue }
    purchased.insert(transaction.productID)
}

// Check specific product (iOS 18.4+)
for await result in Transaction.currentEntitlements(for: productID) {
    if let transaction = try? result.payloadValue,
       transaction.revocationDate == nil { return true }
}

Note: currentEntitlement(for:) (singular) deprecated in iOS 18.4. Use currentEntitlements(for:).

Restore Purchases (REQUIRED)

func restorePurchases() async {
    try? await AppStore.sync()
    await updatePurchasedProducts()
}

App Store requires restore functionality for non-consumables and subscriptions.

Handle Refunds

if let revocationDate = transaction.revocationDate {
    switch transaction.revocationReason {
    case .developerIssue: // App issue
    case .other:          // Other reason
    @unknown default: break
    }
    await revokeEntitlement(for: transaction.productID)
}

Anti-Patterns

Pattern Problem Solution
Only handle in purchase() Misses pending, family sharing, restore Use Transaction.updates
No restore button App Store rejection Provide restore in settings
Not finishing Queue builds up Always call finish()

Source: SKILL.md on GitHub

1 warning16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The storekit skill provides technical documentation and architectural patterns for implementing Apple's StoreKit 2 in-app purchase framework. It emphasizes security best practices such as transaction verification and main-thread safety, and contains no malicious content or suspicious patterns.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    6/6 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at d62eb71. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Dormantupdated 9 months ago

README badge

README badge for johnrogers/claude-swift-engineering/storekit