{"id":"b7425f0d-0b36-42b6-93c0-16fffd921901","shortId":"zLATYD","kind":"skill","title":"Storekit","tagline":"Swift Ios Skills skill by Dpearson2699","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"],"capabilities":["skill","source-dpearson2699","category-swift-ios-skills"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/storekit","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"install_from":"skills.sh"}},"qualityScore":"0.300","qualityRationale":"deterministic score 0.30 from registry signals: · indexed on skills.sh · published under dpearson2699/swift-ios-skills","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:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T05:40:37.837Z","embedding":null,"createdAt":"2026-04-18T20:33:32.918Z","updatedAt":"2026-04-22T05:40:37.837Z","lastSeenAt":"2026-04-22T05:40:37.837Z","tsv":"'1':904 '10':1194 '16':787 '17':76,80,83,87,489,595 '2':9,26,735,942,1021 '26':29 '3':976 '3.1.1':1366 '4':999 '4.99':1087 '5':858,1039 '6':1072 '60':564 '7':1098 '8':1124 '9':1167 'access':175,573,720,1046,1242 'account':838 'across':1355 'activ':398,704 'add':740,1016 'advanc':1385 'also':1159 'alway':246,400,962 'api':38,44,1004,1304 'app':12,19,100,102,106,108,327,349,765,767,775,832,837,909,925,936,1177,1232,1235,1329 'app-transaction-app-purchase-verif':105 'appaccounttoken':264,849,900 'appdidfinish':1138 'appl':713,726 'applic':1338 'appropri':826 'approv':293,341,921,1120 'appstore.requestreview':1036 'appstore.sync':746,755 'apptransact':803 'apptransaction.originalappversion':806 'apptransaction.originalpurchasedate':809 'apptransaction.shared':797 'ask':288,338,860,918,1109,1258 'async':256,422,667,751,791 'auto':160 'auto-renew':159 'automat':166,177,506 'autorenew':162 'avoid':39,1006 'await':224,261,277,281,372,384,386,427,546,630,673,754,756,796,846,854,866,895,974,1025,1032,1035,1119,1140,1162 'behavior':140 'bill':164 'blue.gradient':579 'bodi':360,458,927 'bool':417,669 'boundari':1357 'break':285,299,302,827,1113 'built':491 'built-in':490 'busi':779 'button':605,743,1171,1263 'buy':290,340,862,920,1084,1094,1111,1260 'call':237,745,1244 'callback':118,122,873 'cancel':883 'case':139,245,268,283,286,311,317,377,432,464,467,470,540,624,644,650,682,685,800,819,889,1051,1063,1121 'catch':329,828 'category-swift-ios-skills' 'chang':336,781,1158 'check':70,73,90,94,389,401,661,1041,1125,1133,1145,1325 'checklist':129,132,1227 'checksubscriptionact':664 'checkverifi':275,304,957,970,997 'class':406 'clear':1291,1300 'coin':149 'com.app.premium':1014,1028 'com.myapp':203 'com.myapp.monthly':208 'com.myapp.premium':199 'com.myapp.yearly':212 'common':123,126,902 'common-mistak':125 'concurr':1356 'configur':1316 'constant':185,1311 'consum':141,142,152,395,851,1339 'containerbackground':578 'content':48,297,550,966,1165,1246 'contentview':364,931 'continu':383,689,721 'control':1378 'correct':932,961,990,1058,1088,1114,1142,1185,1211 'could':829 'crown.fill':560 'currenc':1080 'currententitlementtask':444,480 'custom':548 'default':301,653,1112 'defer':292 'defin':181,1309 'deliv':965,1340 'delivercont':278 'deliveri':1247 'detect':783 'devic':333 'display':1292,1368 'dpearson2699':7 'drop':1107 'durat':1289 'els':382,478,688 'entitl':69,72,388,1126,1252,1322 'entitlement-check':71 'entitlementtaskst':454 'enum':138,194 'error':320,322,355,369 'exclud':1250 'expir':706,709 'explicit':747 'fail':712,718 'failur':468 'fals':698,881 'famili':334 'featur':575,824 'fetch':186 'file':1317 'finish':249,948,963,1342 'flow':60,63,236 'font':561,570 'foreground':1150,1330 'foregroundstyl':565,576 'forev':953 'forget':943 'free':1293 'func':252,303,365,420,663,749,789,1137 'gem':148 'gems100':202,204,218 'grace':723 'grant':1045,1241 'group':461,511,555,588,1183,1188,1209,1214 'groupid':509,553,586,665,676,1182,1187,1208,1213 'guard':376,681 'guidelin':1365 'handl':241,498,736,1100,1154,1256,1387 'hardcod':1073,1285 'hierarch':582 'iap':1363 'iconphas':640,643 'id':183,512,556,589,608,638,1184,1189,1210,1215,1308 'ignor':977 'imag':558,647,654 'image.resizable':648 'implement':16 'import':192 'in-app':10,17 'inapppurchaseopt':897 'inbillingretryperiod':710 'individu':634 'inform':1115 'ingraceperiod':694,716 'init':356,937,1236 'instal':776,785 'io':3,28,75,79,82,86,488,594,786 'ispremium':416 'jwsrepresent':1336 'larg':614,658 'largetitle.bold':571 'launch':328,910,1130,1233 'layout':583 'legaci':1001,1009 'legitimaci':772 'let':197,201,206,210,214,221,258,270,272,313,319,379,409,434,472,543,627,646,670,793,802,804,807,892,954,967,987,994,1010,1022,1029,1053,1065 'limit':174 'link':1199,1276 'listen':65,68,324,914,934,1153,1229 'listenfortransact':358,366 'load':54,57,179,456,465,500,651 'loading-product':56 'local':600,1089 'logic':811 'main':346,922 'mainactor':405 'manag':1381 'market':549 'mean':702 'merchandis':596 'mid':1156 'mid-sess':1155 'migrat':810 'miss':915,1168 'mistak':124,127,903 'model':780,818,1349 'modern':32 'modifi':445 'monthlyplan':207,219 'multilin':516 'multipl':597 'myapp':348,924 'name':601 'never':947,1135,1284 'nil':438,476,1069 'non':151,169,394 'non-consum':150,393 'non-renew':168 'nonconsum':153 'nonrenew':171 'observ':404,1345 'offer':1380 'older':41 'oninapppurchasecomplet':535,619,884 'oninapppurchasestart':875 'option':112,115,239,263,835,848,856,868,1176 'origin':42,1002,1007,1302 'originalvers':805 'paid':815 'pattern':1383 'payment':711,717,1017 'paywal':23,497,1266 'paywallview':469,479 'pend':287,1101,1108,1122,1253 'period':724 'perman':156 'picker':592 'polici':1198,1206,1275 'post':1297 'post-trial':1296 'potenti':821 'prefer':1019 'preferspromotionalicon':611 'premium':157,198,217,569,1085 'premiumcontentview':477 'premiumgatedview':448 'prevent':1372 'price':602,1074,1090,1280,1288,1299 'print':232 'privaci':1205,1274 'privacypolici':534,1225 'privacyurl':532,1223 'privat':350,451 'product':33,49,52,55,58,135,180,182,187,222,229,231,254,255,499,536,598,620,635,876,885,898,1023,1092,1307,1348 'product-typ':51 'product.displayname':233,1095 'product.displayprice':234,1097,1283 'product.products':189,225,1026 'product.purchase':238,262,847,855,867,1033 'product.subscriptioninfo.status.status':674 'productid':195 'productid.all':227 'productid.gems100':609 'productid.premium':419,482,610,639 'productidentifi':1013 'productview':632,637 'productviewstyl':613,657 'progressview':466,652 'prominentpick':514 'prompt':1343 'purchas':13,20,59,62,96,99,103,109,111,114,117,121,154,235,253,330,396,424,442,501,505,604,733,768,834,872,1049,1102,1117,1170,1254,1262 'purchase-flow':61 'purchase-opt':113 'purchased':808 'purchased.insert':439,1056,1070 'purchasedproductid':413,441 'purchasedproductids.contains':418 'purchaseresult':244 'quantiti':852,857 'queue':952 're':1144,1324 're-check':1143,1323 'reappear':949 'reconcili':844 'recur':163 'redeemcod':524 'refer':133,134,1358 'references/app-review-guidelines.md':1360,1361 'references/storekit-advanced.md':1374,1375 'refund':342,727,917,1048 'region':1082 'reject':1179,1371 'renew':161,167,170,178,337,699,916,1290 'repurchas':147 'request':1011 'requir':1369 'restor':95,98,504,732,737,742,1169,1175,1261 'restore-purchas':97 'restorepurchas':520,618,750,764,1193 'restrict':823 'result':259,267,305,310,373,381,428,436,537,545,621,629,794,799,886,894,979,1030,1055,1067,1103,1255 'retri':715 'retriev':831 'return':315,695,697,878,880,1151 'review':128,131,1226 'review-checklist':130 'revoc':344 'revocationd':402,1042 'revok':725,729,1060,1248 'risk':986,1180 'rule':1364 'sandbox':864 'scaledtofit':649 'scatter':1313 'scene':362,929 'secondari':577 'section':940 'secur':985 'see':938,1359,1373 'self.state':485 'sendabl':1352 'server':842,1332 'server-sid':841,1331 'servic':1272 'session':1157 'set':414,425,1318 'share':335,410,1354 'shown':1281 'showpendingapprovalmessag':1123 'side':843,1333 'silent':1106 'simul':859 'simulatesasktobuyinsandbox':869 'size':563 'skill':4,5 'skip':1059 'skpaymentqueu':46,1306 'skpaymentqueue.default':1015 'skproduct':45,1305 'skproductsrequest':1012 'skstorereviewcontrol':47 'skstorereviewcontroller.requestreview':1018 'source-dpearson2699' 'star.fill':656 'start':325,906,933,1230 'state':450,453,463,483,486,700,701,1295 'static':196,200,205,209,213,408 'status':89,93,660,671,678,680 'status.renewalinfo':684 'status.state':691,693 'status.transaction':687 'store':759,1178,1268 'storebutton':517,521,615,761,1190 'storekit':1,8,25,43,193,734,1003,1008,1020,1303,1315 'storemanag':407,411 'storemanager.shared.updateentitlements':385,757 'storemanager.updateentitlements':1163 'storeview':35,81,85,593,607 'storeview-io':84 'string':216,666,1314 'struct':347,447,923 'style':1379 'subscrib':692,703 'subscript':15,21,88,92,399,496,659,705,707,731,817,1195,1278,1286,1367,1377,1386 'subscription-status-check':91 'subscriptionperiodgroupset':590 'subscriptionstor':581 'subscriptionstorebuttonlabel':515 'subscriptionstorecontrolstyl':513,591 'subscriptionstorepolicydestin':525,530,1216,1221 'subscriptionstoreview':37,74,78,487,508,552,585,1181,1186,1207,1212 'subscriptionstoreview-io':77 'success':269,471,541,625,645,890 'swift':2,191,251,345,403,446,507,551,584,606,636,662,748,788,836,874,911,945,980,1005,1043,1075,1104,1131,1172,1200 'swiftui':116,120,443,493,871 'swiftui-purchase-callback':119 'switch':266,309,462,642,798 'system':562 'systemnam':559,655 'tamper':784,822 'task':353,367,1139,1161 'task.detached':370 'term':1203,1270,1287 'termsofservic':529,1220 'termsurl':527,1218 'test':1321,1382 'text':567,572,1083,1093 'three':243 'throw':257,307,321,668,752 'time':173 'time-limit':172 'token':839 'transact':34,101,107,250,273,280,380,435,473,475,544,628,766,833,893,955,968,984,988,995,1054,1061,1066,1238,1249,1344 'transaction.currententitlements':391,430,739 'transaction.finish':282,387,547,631,896,944,975,1243 'transaction.productid':440,960,973,1057,1071 'transaction.revocationdate':437,1068 'transaction.updates':64,323,375,907,939,1147,1152,1228,1327 'transactionlisten':352,357 'transactionupd':67 'transactionupdates-listen':66 'tri':223,260,274,672,753,795,845,853,865,956,969,996,1024,1031,1034 'trial':1294,1298 'true':612,696,870,879 'type':50,53,136,137,1346,1350 'ui':502 'unfinish':951 'unknown':300 'unlock':158,296,568 'unlockfeatur':959,972 'unverifi':318,820,983 'updat':1136 'updateentitl':421,1141 'url':526,531,1217,1222 'use':24,30,143,390,777,982,993,1000,1160,1282,1335 'user':813,1116 'useraccounttoken':265,901 'usercancel':284 'uuid':850 'valid':1334 'valu':314,316 'var':351,359,412,415,423,452,457,926 'verif':104,110,271,276,769,958,971,978,998 'verifi':247,312,378,433,542,626,683,686,770,801,891,991,1052,1064,1239 'verification.unsafepayloadvalue':989 'verificationresult':306,455 'verifyapppurchas':790 'via':738 'view':449,460,494,760,1166,1196,1269,1279 'visibl':518,522,616,762,1191,1264 'void':354,368 'vstack':557 'windowgroup':363,930 'windowscen':1038 'without':176,1197 'wrong':912,946,981,1044,1076,1077,1105,1132,1173,1201 'yearlyplan':211,220 'yellow':566 'yet':298","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-04-22T00:53:44.752Z"},{"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-04-22T05:40:37.837Z"}],"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","source":"skills_sh","category":"swift-ios-skills","skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/storekit"},"updatedAt":"2026-04-22T05:40:37.837Z"}}