{"id":"b7425f0d-0b36-42b6-93c0-16fffd921901","shortId":"zLATYD","kind":"skill","title":"storekit","tagline":"Implement, review, or improve in-app purchases and subscriptions using StoreKit 2. Use when building paywalls with SubscriptionStoreView or ProductView, processing transactions with Product and Transaction APIs, verifying entitlements, handling purchase flows (consumable, non-con","description":"# StoreKit 2 In-App Purchases and Subscriptions\n\nImplement in-app purchases, subscriptions, and paywalls using StoreKit 2 on\niOS 26+. Use the modern `Product`, `Transaction`, `StoreView`, and\n`SubscriptionStoreView` APIs. Avoid the older original StoreKit APIs\n(`SKProduct`, `SKPaymentQueue`, `SKStoreReviewController`).\n\n## Contents\n\n- [Product Types](#product-types)\n- [Loading Products](#loading-products)\n- [Purchase Flow](#purchase-flow)\n- [Transaction.updates Listener](#transactionupdates-listener)\n- [Entitlement Checking](#entitlement-checking)\n- [SubscriptionStoreView (iOS 17+)](#subscriptionstoreview-ios-17)\n- [StoreView (iOS 17+)](#storeview-ios-17)\n- [Subscription Status Checking](#subscription-status-checking)\n- [Restore Purchases](#restore-purchases)\n- [App Transaction (App Purchase Verification)](#app-transaction-app-purchase-verification)\n- [Purchase Options](#purchase-options)\n- [SwiftUI Purchase Callbacks](#swiftui-purchase-callbacks)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n## Product Types\n\n| Type | Enum Case | Behavior |\n|---|---|---|\n| **Consumable** | `.consumable` | Used once, can be repurchased (gems, coins) |\n| **Non-consumable** | `.nonConsumable` | Purchased once permanently (premium unlock) |\n| **Auto-renewable** | `.autoRenewable` | Recurring billing with automatic renewal |\n| **Non-renewing** | `.nonRenewing` | Time-limited access without automatic renewal |\n\n## Loading Products\n\nDefine product IDs as constants. Fetch products with `Product.products(for:)`.\n\n```swift\nimport StoreKit\n\nenum ProductID {\n    static let premium = \"com.myapp.premium\"\n    static let gems100 = \"com.myapp.gems100\"\n    static let monthlyPlan = \"com.myapp.monthly\"\n    static let yearlyPlan = \"com.myapp.yearly\"\n    static let all: [String] = [premium, gems100, monthlyPlan, yearlyPlan]\n}\n\nlet products = try await Product.products(for: ProductID.all)\nfor product in products {\n    print(\"\\(product.displayName): \\(product.displayPrice)\")\n}\n```\n\n## Purchase Flow\n\nCall `product.purchase(options:)` and handle all three `PurchaseResult` cases.\nAlways verify and finish transactions.\n\n```swift\nfunc purchase(_ product: Product) async throws {\n    let result = try await product.purchase(options: [\n        .appAccountToken(userAccountToken)\n    ])\n    switch result {\n    case .success(let verification):\n        let transaction = try checkVerified(verification)\n        await deliverContent(for: transaction)\n        await transaction.finish()\n    case .userCancelled:\n        break\n    case .pending:\n        // Ask to Buy or deferred approval -- do not unlock content yet\n        break\n    @unknown default:\n        break\n    }\n}\n\nfunc checkVerified<T>(_ result: VerificationResult<T>) throws -> T {\n    switch result {\n    case .verified(let value): return value\n    case .unverified(_, let error): throw error\n    }\n}\n```\n\n## Transaction.updates Listener\n\nStart at app launch. Catches purchases from other devices, Family Sharing\nchanges, renewals, Ask to Buy approvals, refunds, and revocations.\n\n```swift\n@main\nstruct MyApp: App {\n    private var transactionListener: Task<Void, Error>?\n\n    init() {\n        transactionListener = listenForTransactions()\n    }\n\n    var body: some Scene {\n        WindowGroup { ContentView() }\n    }\n\n    func listenForTransactions() -> Task<Void, Error> {\n        Task.detached {\n            for await result in Transaction.updates {\n                guard case .verified(let transaction) = result else { continue }\n                await StoreManager.shared.updateEntitlements()\n                await transaction.finish()\n            }\n        }\n    }\n}\n```\n\n## Entitlement Checking\n\nUse `Transaction.currentEntitlements` for non-consumable purchases and active\nsubscriptions. Always check `revocationDate`.\n\n```swift\n@Observable\n@MainActor\nclass StoreManager {\n    static let shared = StoreManager()\n    var purchasedProductIDs: Set<String> = []\n    var isPremium: Bool { purchasedProductIDs.contains(ProductID.premium) }\n\n    func updateEntitlements() async {\n        var purchased = Set<String>()\n        for await result in Transaction.currentEntitlements {\n            if case .verified(let transaction) = result,\n               transaction.revocationDate == nil {\n                purchased.insert(transaction.productID)\n            }\n        }\n        purchasedProductIDs = purchased\n    }\n}\n```\n\n### SwiftUI .currentEntitlementTask Modifier\n\n```swift\nstruct PremiumGatedView: View {\n    @State private var state: EntitlementTaskState<VerificationResult<Transaction>?> = .loading\n\n    var body: some View {\n        Group {\n            switch state {\n            case .loading: ProgressView()\n            case .failure: PaywallView()\n            case .success(let transaction):\n                if transaction != nil { PremiumContentView() }\n                else { PaywallView() }\n            }\n        }\n        .currentEntitlementTask(for: ProductID.premium) { state in\n            self.state = state\n        }\n    }\n}\n```\n\n## SubscriptionStoreView (iOS 17+)\n\nBuilt-in SwiftUI view for subscription paywalls. Handles product loading,\npurchase UI, and restore purchases automatically.\n\n```swift\nSubscriptionStoreView(groupID: \"YOUR_GROUP_ID\")\n    .subscriptionStoreControlStyle(.prominentPicker)\n    .subscriptionStoreButtonLabel(.multiline)\n    .storeButton(.visible, for: .restorePurchases)\n    .storeButton(.visible, for: .redeemCode)\n    .subscriptionStorePolicyDestination(url: termsURL, for: .termsOfService)\n    .subscriptionStorePolicyDestination(url: privacyURL, for: .privacyPolicy)\n    .onInAppPurchaseCompletion { product, result in\n        if case .success(.verified(let transaction)) = result {\n            await transaction.finish()\n        }\n    }\n```\n\n### Custom Marketing Content\n\n```swift\nSubscriptionStoreView(groupID: \"YOUR_GROUP_ID\") {\n    VStack {\n        Image(systemName: \"crown.fill\").font(.system(size: 60)).foregroundStyle(.yellow)\n        Text(\"Unlock Premium\").font(.largeTitle.bold())\n        Text(\"Access all features\").foregroundStyle(.secondary)\n    }\n}\n.containerBackground(.blue.gradient, for: .subscriptionStore)\n```\n\n### Hierarchical Layout\n\n```swift\nSubscriptionStoreView(groupID: \"YOUR_GROUP_ID\") {\n    SubscriptionPeriodGroupSet()\n}\n.subscriptionStoreControlStyle(.picker)\n```\n\n## StoreView (iOS 17+)\n\nMerchandises multiple products with localized names, prices, and purchase buttons.\n\n```swift\nStoreView(ids: [ProductID.gems100, ProductID.premium], prefersPromotionalIcon: true)\n    .productViewStyle(.large)\n    .storeButton(.visible, for: .restorePurchases)\n    .onInAppPurchaseCompletion { product, result in\n        if case .success(.verified(let transaction)) = result {\n            await transaction.finish()\n        }\n    }\n```\n\n### ProductView for Individual Products\n\n```swift\nProductView(id: ProductID.premium) { iconPhase in\n    switch iconPhase {\n    case .success(let image): image.resizable().scaledToFit()\n    case .loading: ProgressView()\n    default: Image(systemName: \"star.fill\")\n    }\n}\n.productViewStyle(.large)\n```\n\n## Subscription Status Checking\n\n```swift\nfunc checkSubscriptionActive(groupID: String) async throws -> Bool {\n    let statuses = try await Product.SubscriptionInfo.Status.status(for: groupID)\n    for status in statuses {\n        guard case .verified = status.renewalInfo,\n              case .verified = status.transaction else { continue }\n        if status.state == .subscribed || status.state == .inGracePeriod {\n            return true\n        }\n    }\n    return false\n}\n```\n\n### Renewal States\n\n| State | Meaning |\n|---|---|\n| `.subscribed` | Active subscription |\n| `.expired` | Subscription has expired |\n| `.inBillingRetryPeriod` | Payment failed, Apple is retrying |\n| `.inGracePeriod` | Payment failed but access continues during grace period |\n| `.revoked` | Apple refunded or revoked the subscription |\n\n## Restore Purchases\n\nStoreKit 2 handles restoration via `Transaction.currentEntitlements`. Add a\nrestore button or call `AppStore.sync()` explicitly.\n\n```swift\nfunc restorePurchases() async throws {\n    try await AppStore.sync()\n    await StoreManager.shared.updateEntitlements()\n}\n```\n\nOn store views: `.storeButton(.visible, for: .restorePurchases)`\n\n## App Transaction (App Purchase Verification)\n\nVerify the legitimacy of the app installation. Use for business model changes\nor detecting tampered installations (iOS 16+).\n\n```swift\nfunc verifyAppPurchase() async {\n    do {\n        let result = try await AppTransaction.shared\n        switch result {\n        case .verified(let appTransaction):\n            let originalVersion = appTransaction.originalAppVersion\n            let purchaseDate = appTransaction.originalPurchaseDate\n            // Migration logic for users who paid before subscription model\n        case .unverified:\n            // Potentially tampered -- restrict features as appropriate\n            break\n        }\n    } catch { /* Could not retrieve app transaction */ }\n}\n```\n\n## Purchase Options\n\n```swift\n// App account token for server-side reconciliation\ntry await product.purchase(options: [.appAccountToken(UUID())])\n\n// Consumable quantity\ntry await product.purchase(options: [.quantity(5)])\n\n// Simulate Ask to Buy in sandbox\ntry await product.purchase(options: [.simulatesAskToBuyInSandbox(true)])\n```\n\n## SwiftUI Purchase Callbacks\n\n```swift\n.onInAppPurchaseStart { product in\n    return true  // Return false to cancel\n}\n.onInAppPurchaseCompletion { product, result in\n    if case .success(.verified(let transaction)) = result {\n        await transaction.finish()\n    }\n}\n.inAppPurchaseOptions { product in\n    [.appAccountToken(userAccountToken)]\n}\n```\n\n## Common Mistakes\n\n### 1. Not starting Transaction.updates at app launch\n\n```swift\n// WRONG: No listener -- misses renewals, refunds, Ask to Buy approvals\n@main struct MyApp: App {\n    var body: some Scene { WindowGroup { ContentView() } }\n}\n// CORRECT: Start listener in App init (see Transaction.updates section above)\n```\n\n### 2. Forgetting transaction.finish()\n\n```swift\n// WRONG: Never finished -- reappears in unfinished queue forever\nlet transaction = try checkVerified(verification)\nunlockFeature(transaction.productID)\n\n// CORRECT: Always finish after delivering content\nlet transaction = try checkVerified(verification)\nunlockFeature(transaction.productID)\nawait transaction.finish()\n```\n\n### 3. Ignoring verification result\n\n```swift\n// WRONG: Using unverified transaction -- security risk\nlet transaction = verification.unsafePayloadValue\n\n// CORRECT: Verify before using\nlet transaction = try checkVerified(verification)\n```\n\n### 4. Using legacy original StoreKit APIs\n\n```swift\n// AVOID: Original StoreKit (legacy)\nlet request = SKProductsRequest(productIdentifiers: [\"com.app.premium\"])\nSKPaymentQueue.default().add(payment)\nSKStoreReviewController.requestReview()\n\n// PREFERRED: StoreKit 2\nlet products = try await Product.products(for: [\"com.app.premium\"])\nlet result = try await product.purchase()\ntry await AppStore.requestReview(in: windowScene)\n```\n\n### 5. Not checking revocationDate\n\n```swift\n// WRONG: Grants access to refunded purchases\nif case .verified(let transaction) = result {\n    purchased.insert(transaction.productID)\n}\n\n// CORRECT: Skip revoked transactions\nif case .verified(let transaction) = result, transaction.revocationDate == nil {\n    purchased.insert(transaction.productID)\n}\n```\n\n### 6. Hardcoding prices\n\n```swift\n// WRONG: Wrong for other currencies and regions\nText(\"Buy Premium for $4.99\")\n\n// CORRECT: Localized price from Product\nText(\"Buy \\(product.displayName) for \\(product.displayPrice)\")\n```\n\n### 7. Not handling .pending purchase result\n\n```swift\n// WRONG: Silently drops pending Ask to Buy\ndefault: break\n\n// CORRECT: Inform user purchase is awaiting approval\ncase .pending:\n    showPendingApprovalMessage()\n```\n\n### 8. Checking entitlements only once at launch\n\n```swift\n// WRONG: Check once, never update\nfunc appDidFinish() { Task { await updateEntitlements() } }\n\n// CORRECT: Re-check on Transaction.updates AND on foreground return\n// Transaction.updates listener handles mid-session changes.\n// Also use .task { await storeManager.updateEntitlements() } on content views.\n```\n\n### 9. Missing restore purchases button\n\n```swift\n// WRONG: No restore option -- App Store rejection risk\nSubscriptionStoreView(groupID: \"group_id\")\n\n// CORRECT\nSubscriptionStoreView(groupID: \"group_id\")\n    .storeButton(.visible, for: .restorePurchases)\n```\n\n### 10. Subscription views without policy links\n\n```swift\n// WRONG: No terms or privacy policy\nSubscriptionStoreView(groupID: \"group_id\")\n\n// CORRECT\nSubscriptionStoreView(groupID: \"group_id\")\n    .subscriptionStorePolicyDestination(url: termsURL, for: .termsOfService)\n    .subscriptionStorePolicyDestination(url: privacyURL, for: .privacyPolicy)\n```\n\n## Review Checklist\n\n- [ ] `Transaction.updates` listener starts at app launch in App init\n- [ ] All transactions verified before granting access\n- [ ] `transaction.finish()` called after content delivery\n- [ ] Revoked transactions excluded from entitlements\n- [ ] `.pending` purchase result handled for Ask to Buy\n- [ ] Restore purchases button visible on paywall and store views\n- [ ] Terms of Service and Privacy Policy links on subscription views\n- [ ] Prices shown using `product.displayPrice`, never hardcoded\n- [ ] Subscription terms (price, duration, renewal) clearly displayed\n- [ ] Free trial states post-trial pricing clearly\n- [ ] No original StoreKit APIs (`SKProduct`, `SKPaymentQueue`)\n- [ ] Product IDs defined as constants, not scattered strings\n- [ ] StoreKit configuration file set up for testing\n- [ ] Entitlements re-checked on Transaction.updates and app foreground\n- [ ] Server-side validation uses `jwsRepresentation` if applicable\n- [ ] Consumables delivered and finished promptly\n- [ ] Transaction observer types and product model types are `Sendable` when shared across concurrency boundaries\n\n## References\n\n- See [references/app-review-guidelines.md](references/app-review-guidelines.md) for IAP rules (Guideline 3.1.1),\n  subscription display requirements, and rejection prevention.\n- See [references/storekit-advanced.md](references/storekit-advanced.md) for subscription control styles,\n  offer management, testing patterns, and advanced subscription handling.","tags":["storekit","swift","ios","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills"],"capabilities":["skill","source-dpearson2699","skill-storekit","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/storekit","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 599 github stars · SKILL.md body (14,368 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:53:44.387Z","embedding":null,"createdAt":"2026-04-18T20:33:32.918Z","updatedAt":"2026-05-18T18:53:44.387Z","lastSeenAt":"2026-05-18T18:53:44.387Z","tsv":"'1':935 '10':1225 '16':818 '17':107,111,114,118,520,626 '2':14,40,57,766,973,1052 '26':60 '3':1007 '3.1.1':1397 '4':1030 '4.99':1118 '5':889,1070 '6':1103 '60':595 '7':1129 '8':1155 '9':1198 'access':206,604,751,1077,1273 'account':869 'across':1386 'activ':429,735 'add':771,1047 'advanc':1416 'also':1190 'alway':277,431,993 'api':29,69,75,1035,1335 'app':8,43,50,131,133,137,139,358,380,796,798,806,863,868,940,956,967,1208,1263,1266,1360 'app-transaction-app-purchase-verif':136 'appaccounttoken':295,880,931 'appdidfinish':1169 'appl':744,757 'applic':1369 'appropri':857 'approv':324,372,952,1151 'appstore.requestreview':1067 'appstore.sync':777,786 'apptransact':834 'apptransaction.originalappversion':837 'apptransaction.originalpurchasedate':840 'apptransaction.shared':828 'ask':319,369,891,949,1140,1289 'async':287,453,698,782,822 'auto':191 'auto-renew':190 'automat':197,208,537 'autorenew':193 'avoid':70,1037 'await':255,292,308,312,403,415,417,458,577,661,704,785,787,827,877,885,897,926,1005,1056,1063,1066,1150,1171,1193 'behavior':171 'bill':195 'blue.gradient':610 'bodi':391,489,958 'bool':448,700 'boundari':1388 'break':316,330,333,858,1144 'build':17 'built':522 'built-in':521 'busi':810 'button':636,774,1202,1294 'buy':321,371,893,951,1115,1125,1142,1291 'call':268,776,1275 'callback':149,153,904 'cancel':914 'case':170,276,299,314,317,342,348,408,463,495,498,501,571,655,675,681,713,716,831,850,920,1082,1094,1152 'catch':360,859 'chang':367,812,1189 'check':101,104,121,125,420,432,692,1072,1156,1164,1176,1356 'checklist':160,163,1258 'checksubscriptionact':695 'checkverifi':306,335,988,1001,1028 'class':437 'clear':1322,1331 'coin':180 'com.app.premium':1045,1059 'com.myapp':234 'com.myapp.monthly':239 'com.myapp.premium':230 'com.myapp.yearly':243 'common':154,157,933 'common-mistak':156 'con':38 'concurr':1387 'configur':1347 'constant':216,1342 'consum':35,172,173,183,426,882,1370 'containerbackground':609 'content':79,328,581,997,1196,1277 'contentview':395,962 'continu':414,720,752 'control':1409 'correct':963,992,1021,1089,1119,1145,1173,1216,1242 'could':860 'crown.fill':591 'currenc':1111 'currententitlementtask':475,511 'custom':579 'default':332,684,1143 'defer':323 'defin':212,1340 'deliv':996,1371 'delivercont':309 'deliveri':1278 'detect':814 'devic':364 'display':1323,1399 'drop':1138 'durat':1320 'els':413,509,719 'entitl':31,100,103,419,1157,1283,1353 'entitlement-check':102 'entitlementtaskst':485 'enum':169,225 'error':351,353,386,400 'exclud':1281 'expir':737,740 'explicit':778 'fail':743,749 'failur':499 'fals':729,912 'famili':365 'featur':606,855 'fetch':217 'file':1348 'finish':280,979,994,1373 'flow':34,91,94,267 'font':592,601 'foreground':1181,1361 'foregroundstyl':596,607 'forev':984 'forget':974 'free':1324 'func':283,334,396,451,694,780,820,1168 'gem':179 'gems100':233,235,249 'grace':754 'grant':1076,1272 'group':492,542,586,619,1214,1219,1240,1245 'groupid':540,584,617,696,707,1213,1218,1239,1244 'guard':407,712 'guidelin':1396 'handl':32,272,529,767,1131,1185,1287,1418 'hardcod':1104,1316 'hierarch':613 'iap':1394 'iconphas':671,674 'id':214,543,587,620,639,669,1215,1220,1241,1246,1339 'ignor':1008 'imag':589,678,685 'image.resizable':679 'implement':2,47 'import':223 'improv':5 'in-app':6,41,48 'inapppurchaseopt':928 'inbillingretryperiod':741 'individu':665 'inform':1146 'ingraceperiod':725,747 'init':387,968,1267 'instal':807,816 'io':59,106,110,113,117,519,625,817 'ispremium':447 'jwsrepresent':1367 'larg':645,689 'largetitle.bold':602 'launch':359,941,1161,1264 'layout':614 'legaci':1032,1040 'legitimaci':803 'let':228,232,237,241,245,252,289,301,303,344,350,410,440,465,503,574,658,677,701,824,833,835,838,923,985,998,1018,1025,1041,1053,1060,1084,1096 'limit':205 'link':1230,1307 'listen':96,99,355,945,965,1184,1260 'listenfortransact':389,397 'load':85,88,210,487,496,531,682 'loading-product':87 'local':631,1120 'logic':842 'main':377,953 'mainactor':436 'manag':1412 'market':580 'mean':733 'merchandis':627 'mid':1187 'mid-sess':1186 'migrat':841 'miss':946,1199 'mistak':155,158,934 'model':811,849,1380 'modern':63 'modifi':476 'monthlyplan':238,250 'multilin':547 'multipl':628 'myapp':379,955 'name':632 'never':978,1166,1315 'nil':469,507,1100 'non':37,182,200,425 'non-con':36 'non-consum':181,424 'non-renew':199 'nonconsum':184 'nonrenew':202 'observ':435,1376 'offer':1411 'older':72 'oninapppurchasecomplet':566,650,915 'oninapppurchasestart':906 'option':143,146,270,294,866,879,887,899,1207 'origin':73,1033,1038,1333 'originalvers':836 'paid':846 'pattern':1414 'payment':742,748,1048 'paywal':18,54,528,1297 'paywallview':500,510 'pend':318,1132,1139,1153,1284 'period':755 'perman':187 'picker':623 'polici':1229,1237,1306 'post':1328 'post-trial':1327 'potenti':852 'prefer':1050 'preferspromotionalicon':642 'premium':188,229,248,600,1116 'premiumcontentview':508 'premiumgatedview':479 'prevent':1403 'price':633,1105,1121,1311,1319,1330 'print':263 'privaci':1236,1305 'privacypolici':565,1256 'privacyurl':563,1254 'privat':381,482 'process':23 'product':26,64,80,83,86,89,166,211,213,218,253,260,262,285,286,530,567,629,651,666,907,916,929,1054,1123,1338,1379 'product-typ':82 'product.displayname':264,1126 'product.displayprice':265,1128,1314 'product.products':220,256,1057 'product.purchase':269,293,878,886,898,1064 'product.subscriptioninfo.status.status':705 'productid':226 'productid.all':258 'productid.gems100':640 'productid.premium':450,513,641,670 'productidentifi':1044 'productview':22,663,668 'productviewstyl':644,688 'progressview':497,683 'prominentpick':545 'prompt':1374 'purchas':9,33,44,51,90,93,127,130,134,140,142,145,148,152,185,266,284,361,427,455,473,532,536,635,764,799,865,903,1080,1133,1148,1201,1285,1293 'purchase-flow':92 'purchase-opt':144 'purchased':839 'purchased.insert':470,1087,1101 'purchasedproductid':444,472 'purchasedproductids.contains':449 'purchaseresult':275 'quantiti':883,888 'queue':983 're':1175,1355 're-check':1174,1354 'reappear':980 'reconcili':875 'recur':194 'redeemcod':555 'refer':164,165,1389 'references/app-review-guidelines.md':1391,1392 'references/storekit-advanced.md':1405,1406 'refund':373,758,948,1079 'region':1113 'reject':1210,1402 'renew':192,198,201,209,368,730,947,1321 'repurchas':178 'request':1042 'requir':1400 'restor':126,129,535,763,768,773,1200,1206,1292 'restore-purchas':128 'restorepurchas':551,649,781,795,1224 'restrict':854 'result':290,298,336,341,404,412,459,467,568,576,652,660,825,830,917,925,1010,1061,1086,1098,1134,1286 'retri':746 'retriev':862 'return':346,726,728,909,911,1182 'review':3,159,162,1257 'review-checklist':161 'revoc':375 'revocationd':433,1073 'revok':756,760,1091,1279 'risk':1017,1211 'rule':1395 'sandbox':895 'scaledtofit':680 'scatter':1344 'scene':393,960 'secondari':608 'section':971 'secur':1016 'see':969,1390,1404 'self.state':516 'sendabl':1383 'server':873,1363 'server-sid':872,1362 'servic':1303 'session':1188 'set':445,456,1349 'share':366,441,1385 'shown':1312 'showpendingapprovalmessag':1154 'side':874,1364 'silent':1137 'simul':890 'simulatesasktobuyinsandbox':900 'size':594 'skill' 'skill-storekit' 'skip':1090 'skpaymentqueu':77,1337 'skpaymentqueue.default':1046 'skproduct':76,1336 'skproductsrequest':1043 'skstorereviewcontrol':78 'skstorereviewcontroller.requestreview':1049 'source-dpearson2699' 'star.fill':687 'start':356,937,964,1261 'state':481,484,494,514,517,731,732,1326 'static':227,231,236,240,244,439 'status':120,124,691,702,709,711 'status.renewalinfo':715 'status.state':722,724 'status.transaction':718 'store':790,1209,1299 'storebutton':548,552,646,792,1221 'storekit':1,13,39,56,74,224,765,1034,1039,1051,1334,1346 'storemanag':438,442 'storemanager.shared.updateentitlements':416,788 'storemanager.updateentitlements':1194 'storeview':66,112,116,624,638 'storeview-io':115 'string':247,697,1345 'struct':378,478,954 'style':1410 'subscrib':723,734 'subscript':11,46,52,119,123,430,527,690,736,738,762,848,1226,1309,1317,1398,1408,1417 'subscription-status-check':122 'subscriptionperiodgroupset':621 'subscriptionstor':612 'subscriptionstorebuttonlabel':546 'subscriptionstorecontrolstyl':544,622 'subscriptionstorepolicydestin':556,561,1247,1252 'subscriptionstoreview':20,68,105,109,518,539,583,616,1212,1217,1238,1243 'subscriptionstoreview-io':108 'success':300,502,572,656,676,921 'swift':222,282,376,434,477,538,582,615,637,667,693,779,819,867,905,942,976,1011,1036,1074,1106,1135,1162,1203,1231 'swiftui':147,151,474,524,902 'swiftui-purchase-callback':150 'switch':297,340,493,673,829 'system':593 'systemnam':590,686 'tamper':815,853 'task':384,398,1170,1192 'task.detached':401 'term':1234,1301,1318 'termsofservic':560,1251 'termsurl':558,1249 'test':1352,1413 'text':598,603,1114,1124 'three':274 'throw':288,338,352,699,783 'time':204 'time-limit':203 'token':870 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'transact':24,28,65,132,138,281,304,311,411,466,504,506,575,659,797,864,924,986,999,1015,1019,1026,1085,1092,1097,1269,1280,1375 'transaction.currententitlements':422,461,770 'transaction.finish':313,418,578,662,927,975,1006,1274 'transaction.productid':471,991,1004,1088,1102 'transaction.revocationdate':468,1099 'transaction.updates':95,354,406,938,970,1178,1183,1259,1358 'transactionlisten':383,388 'transactionupd':98 'transactionupdates-listen':97 'tri':254,291,305,703,784,826,876,884,896,987,1000,1027,1055,1062,1065 'trial':1325,1329 'true':643,727,901,910 'type':81,84,167,168,1377,1381 'ui':533 'unfinish':982 'unknown':331 'unlock':189,327,599 'unlockfeatur':990,1003 'unverifi':349,851,1014 'updat':1167 'updateentitl':452,1172 'url':557,562,1248,1253 'use':12,15,55,61,174,421,808,1013,1024,1031,1191,1313,1366 'user':844,1147 'useraccounttoken':296,932 'usercancel':315 'uuid':881 'valid':1365 'valu':345,347 'var':382,390,443,446,454,483,488,957 'verif':135,141,302,307,800,989,1002,1009,1029 'verifi':30,278,343,409,464,573,657,714,717,801,832,922,1022,1083,1095,1270 'verification.unsafepayloadvalue':1020 'verificationresult':337,486 'verifyapppurchas':821 'via':769 'view':480,491,525,791,1197,1227,1300,1310 'visibl':549,553,647,793,1222,1295 'void':385,399 'vstack':588 'windowgroup':394,961 'windowscen':1069 'without':207,1228 'wrong':943,977,1012,1075,1107,1108,1136,1163,1204,1232 'yearlyplan':242,251 'yellow':597 'yet':329","prices":[{"id":"4367c6e4-e24f-442c-9cb3-0faa63fc1f38","listingId":"b7425f0d-0b36-42b6-93c0-16fffd921901","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:33:32.918Z"}],"sources":[{"listingId":"b7425f0d-0b36-42b6-93c0-16fffd921901","source":"github","sourceId":"dpearson2699/swift-ios-skills/storekit","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/storekit","isPrimary":false,"firstSeenAt":"2026-04-18T22:01:18.480Z","lastSeenAt":"2026-05-18T18:53:44.387Z"},{"listingId":"b7425f0d-0b36-42b6-93c0-16fffd921901","source":"skills_sh","sourceId":"dpearson2699/swift-ios-skills/storekit","sourceUrl":"https://skills.sh/dpearson2699/swift-ios-skills/storekit","isPrimary":true,"firstSeenAt":"2026-04-18T20:33:32.918Z","lastSeenAt":"2026-05-07T22:40:32.350Z"}],"details":{"listingId":"b7425f0d-0b36-42b6-93c0-16fffd921901","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"storekit","github":{"repo":"dpearson2699/swift-ios-skills","stars":599,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-26T21:04:17Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"7273f9ebf5b600d837adb818f69339ff8358898d","skill_md_path":"skills/storekit/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/storekit"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"storekit","description":"Implement, review, or improve in-app purchases and subscriptions using StoreKit 2. Use when building paywalls with SubscriptionStoreView or ProductView, processing transactions with Product and Transaction APIs, verifying entitlements, handling purchase flows (consumable, non-consumable, auto-renewable), implementing offer codes or promotional/win-back/introductory offers, managing subscription status and renewal state, setting up StoreKit testing with configuration files, or integrating Family Sharing, Ask to Buy, refund handling, and billing retry logic."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/storekit"},"updatedAt":"2026-05-18T18:53:44.387Z"}}