Skip to main content
ClaudeWave
Skill732 repo starsupdated 15d ago

storekit

StoreKit 2 provides modern Swift APIs for implementing in-app purchases, subscriptions, and paywalls on iOS 16 and later. Use this skill when building product catalogs with `Product` and `Transaction` APIs, creating purchase flows with `PurchaseAction` or `SubscriptionStoreView`, verifying entitlements via `Transaction.currentEntitlements`, setting up transaction listeners with `Transaction.updates`, implementing offer codes and promotional offers, managing subscription status and renewal states, configuring StoreKit testing, and handling Family Sharing and billing retry logic.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/dpearson2699/swift-ios-skills /tmp/storekit && cp -r /tmp/storekit/skills/storekit ~/.claude/skills/storekit
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# StoreKit 2 In-App Purchases and Subscriptions

Implement in-app purchases, subscriptions, paywalls, and StoreKit testing using
StoreKit 2 on iOS 26+. Use the modern Swift-based `Product`, `Transaction`,
`PurchaseAction`, `StoreView`, and `SubscriptionStoreView` APIs. Avoid original
In-App Purchase APIs (`SKProduct`, `SKPaymentQueue`) unless legacy OS support
requires them.

When reviewing StoreKit code, explicitly separate "preferred SwiftUI path" from
"invalid API": `PurchaseAction` is the preferred custom SwiftUI button path, but
direct `product.purchase(options:)` is still valid for lower-level custom
StoreKit flows.

## Contents

- [Implementation Review Minimums](#implementation-review-minimums)
- [Product Types](#product-types)
- [Loading Products](#loading-products)
- [Purchase Flow](#purchase-flow)
- [Transaction.updates Listener](#transactionupdates-listener)
- [Entitlement Checking](#entitlement-checking)
- [SubscriptionStoreView (iOS 17+)](#subscriptionstoreview-ios-17)
- [StoreView (iOS 17+)](#storeview-ios-17)
- [Subscription Status Checking](#subscription-status-checking)
- [Restore Purchases](#restore-purchases)
- [App Transaction (App Purchase Verification)](#app-transaction-app-purchase-verification)
- [Purchase Options](#purchase-options)
- [SwiftUI Purchase Callbacks](#swiftui-purchase-callbacks)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)

## Implementation Review Minimums

When reviewing a paywall, purchase manager, or entitlement gate, include these
points explicitly:

- Standard SwiftUI paywalls should prefer `StoreView`, `ProductView`, or
  `SubscriptionStoreView`; custom SwiftUI buy buttons should prefer
  `PurchaseAction`; direct `product.purchase(options:)` is valid for
  lower-level custom StoreKit flows.
- `Transaction.updates` must start at app launch because it catches purchases
  from other devices, Family Sharing changes, renewals, Ask to Buy approvals,
  refunds, revocations, and unfinished transactions.
- Include this entitlement-scope sentence verbatim when reviewing
  `Transaction.currentEntitlements`: "It covers non-consumables, active or
  grace-period auto-renewable subscriptions, and non-renewing subscriptions; it
  does not include consumable purchase or delivery history."
- Verify every `VerificationResult` before granting access. Deliver or persist
  the entitlement first, then call `transaction.finish()`.
- Pending purchases and user cancellations never unlock content; pending Ask to
  Buy approvals unlock only after a verified transaction arrives through the
  launch-time listener.
- Exclude refunded or revoked transactions from active entitlement state and
  re-check entitlements when refunds or revocations arrive through
  `Transaction.updates`.
- Provide a visible restore purchases path and Terms of Service / Privacy
  Policy links on subscription paywalls.

## Product Types

| Type | Enum Case | Behavior |
|---|---|---|
| **Consumable** | `.consumable` | Used once, can be repurchased (gems, coins) |
| **Non-consumable** | `.nonConsumable` | Purchased once permanently (premium unlock) |
| **Auto-renewable** | `.autoRenewable` | Recurring billing with automatic renewal |
| **Non-renewing** | `.nonRenewing` | Time-limited access without automatic renewal |

## Loading Products

Define product IDs as constants. Fetch products with `Product.products(for:)`.

```swift
import StoreKit

enum ProductID {
    static let premium = "com.myapp.premium"
    static let gems100 = "com.myapp.gems100"
    static let monthlyPlan = "com.myapp.monthly"
    static let yearlyPlan = "com.myapp.yearly"
    static let all: [String] = [premium, gems100, monthlyPlan, yearlyPlan]
}

let products = try await Product.products(for: ProductID.all)
for product in products {
    print("\(product.displayName): \(product.displayPrice)")
}
```

## Purchase Flow

Prefer StoreKit views for standard paywalls because they initiate purchases,
restore purchases, and display policy controls. For custom SwiftUI purchase
buttons, prefer `PurchaseAction` from the environment. Use direct
`product.purchase(options:)` only for lower-level custom flows, and use
`purchase(confirmIn:options:)` for UIKit or AppKit confirmation. Always handle
every `PurchaseResult`, verify before access, deliver durably, then finish.

Review wording: do not call `product.purchase(options:)` inherently wrong. Say
"prefer `PurchaseAction` for SwiftUI buttons; keep `product.purchase(options:)`
for lower-level custom flows that need direct StoreKit control."

```swift
@Environment(\.purchase) private var purchase

func purchaseProduct(_ product: Product) async throws {
    let result = try await purchase(product, options: [
        .appAccountToken(userAccountToken)
    ])
    switch result {
    case .success(let verification):
        let transaction = try checkVerified(verification)
        await deliverContent(for: transaction)
        await transaction.finish()
    case .userCancelled:
        break
    case .pending:
        // Ask to Buy or deferred approval: show pending UI, no unlock yet.
        showPendingApprovalMessage()
    @unknown default:
        break
    }
}

func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
    switch result {
    case .verified(let value): return value
    case .unverified(_, let error): throw error
    }
}
```

## Transaction.updates Listener

Start at app launch, not when a paywall appears. Catches purchases from other
devices, Family Sharing changes, renewals, Ask to Buy approvals, refunds,
revocations, and unfinished transactions Apple emits once immediately after
launch. Keep the task retained for the app lifetime.

In implementation reviews, name the launch-time coverage explicitly: purchases
made on other devices, Family Sharing changes, subscription renewals, Ask to Buy
approvals, refunds, revocations, and unfinished transactions.

```swift
@main
struct MyApp: App {
    private let tran
accessorysetupkitSkill

Discover and configure Bluetooth and Wi-Fi accessories using AccessorySetupKit. Use when presenting a privacy-preserving accessory picker, defining discovery descriptors for BLE or Wi-Fi devices, handling accessory session events, migrating from CoreBluetooth permission-based scanning, or setting up accessories without requiring broad Bluetooth permissions.

activitykitSkill

Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time updating widgets for the Lock Screen and Dynamic Island — delivery tracking, sports scores, ride-sharing status, workout timers, media playback, or any time-sensitive information that updates in real time. Also use when working with ActivityKit, ActivityAttributes, Activity lifecycle (request/update/end), Dynamic Island layouts (compact/minimal/expanded), push-to-update Live Activities, or Lock Screen live widgets.

adattributionkitSkill

Measure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks, updating conversion values, implementing re-engagement attribution, configuring publisher or advertiser apps, or replacing SKAdNetwork with AdAttributionKit for ad measurement.

alarmkitSkill

Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system UI. Covers AlarmManager scheduling, AlarmAttributes and AlarmPresentation, AlarmButton stop and snooze actions, authorization, state observation, countdown widget-extension handoff, and Live Activity integration. Use when building wake-up alarms, countdown timers, or alarm-style alerts that need Apple's system alarm experience.

app-clipsSkill

Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store Connect experiences, size/capability constraints, NSUserActivity routing, SKOverlay promotion, App Group/keychain handoff, ephemeral notifications, location confirmation, and full-app migration. Use when creating App Clips or wiring App Clip invocation, experience configuration, or full-app handoff.

app-intentsSkill

Implement App Intents for Siri, Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence on iOS. Covers AppIntent actions, AppEntity and EntityQuery models, AppShortcutsProvider phrases, IndexedEntity Spotlight indexing, WidgetConfigurationIntent, SnippetIntent, and assistant schemas. Use when exposing app actions or entities to system surfaces.

app-store-optimizationSkill

Optimize App Store product pages for search visibility and conversion. Use for App Store Optimization (ASO), keyword research, app name/subtitle/keyword-field strategy, conversion-focused descriptions and promotional text, screenshot captions and ordering, Custom Product Pages with assigned search keywords, In-App Events, Product Page Optimization tests, localized metadata, ratings/review strategy, and in-app review prompt timing with RequestReviewAction or AppStore.requestReview. Also use when routing ASO vs App Store review, privacy/ATT, or StoreKit implementation boundaries.

app-store-reviewSkill

Prepare for App Store review and prevent rejections. Covers App Store review guidelines, app rejection reasons, PrivacyInfo.xcprivacy privacy manifest requirements, required API reason codes, in-app purchase IAP and StoreKit rules, App Store Guidelines compliance, ATT App Tracking Transparency, EU DMA Digital Markets Act, HIG compliance checklist, app submission preparation, review preparation, metadata requirements, entitlements, widgets, and Live Activities review rules. Use when preparing for App Store submission, fixing rejection reasons, auditing privacy manifests, implementing ATT consent flow, configuring StoreKit IAP, or checking HIG compliance.