{"id":1773,"url":"https://github.com/bizz84/SwiftyStoreKit","last_synced_at":"2025-08-06T13:31:47.012Z","repository":{"id":1219716,"uuid":"41854508","full_name":"bizz84/SwiftyStoreKit","owner":"bizz84","description":"Lightweight In App Purchases Swift framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+ ⛺","archived":false,"fork":false,"pushed_at":"2024-06-28T11:57:34.000Z","size":3864,"stargazers_count":6576,"open_issues_count":192,"forks_count":795,"subscribers_count":127,"default_branch":"master","last_synced_at":"2024-12-02T18:12:06.827Z","etag":null,"topics":["apple","iap","in-app-purchase","in-app-receipt","ios","macos","swift-3","swift-language","tvos"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bizz84.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-09-03T10:40:34.000Z","updated_at":"2024-11-29T14:31:26.000Z","dependencies_parsed_at":"2024-06-18T10:44:27.757Z","dependency_job_id":"f063c9cc-77aa-42fb-b4b0-b96bc08a8664","html_url":"https://github.com/bizz84/SwiftyStoreKit","commit_stats":{"total_commits":683,"total_committers":70,"mean_commits":9.757142857142858,"dds":0.2635431918008785,"last_synced_commit":"48d4d4c0b6a66732eb45e2f0104fb7d080c67a69"},"previous_names":[],"tags_count":72,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizz84%2FSwiftyStoreKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizz84%2FSwiftyStoreKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizz84%2FSwiftyStoreKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bizz84%2FSwiftyStoreKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bizz84","download_url":"https://codeload.github.com/bizz84/SwiftyStoreKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228905429,"owners_count":17989761,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple","iap","in-app-purchase","in-app-receipt","ios","macos","swift-3","swift-language","tvos"],"created_at":"2024-01-05T20:15:55.481Z","updated_at":"2024-12-09T14:30:36.606Z","avatar_url":"https://github.com/bizz84.png","language":"Swift","readme":"\u003cimg src=\"https://github.com/bizz84/SwiftyStoreKit/raw/master/SwiftyStoreKit-logo.png\" width=100%\u003e\n\n[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://mit-license.org)\n[![Platform](http://img.shields.io/badge/platform-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS-lightgrey.svg?style=flat)](https://developer.apple.com/resources/)\n[![Language](https://img.shields.io/badge/swift-5.0-orange.svg)](https://developer.apple.com/swift)\n[![Build](https://img.shields.io/travis/bizz84/SwiftyStoreKit.svg?style=flat)](https://travis-ci.org/bizz84/SwiftyStoreKit)\n[![Issues](https://img.shields.io/github/issues/bizz84/SwiftyStoreKit.svg?style=flat)](https://github.com/bizz84/SwiftyStoreKit/issues)\n[![Slack](https://img.shields.io/badge/Slack-Join-green.svg?style=flat)](https://join.slack.com/t/swiftystorekit/shared_invite/enQtODY3OTYxOTExMzE5LWVkNGY4MzcwY2VjNGM4MGU4NDFhMGE5YmUxMGM3ZTQ4NjVjNTRkNTJhNDAyMWZmY2M5OWE5MDE0ODc3OGJjMmM)\n\nSwiftyStoreKit is a lightweight In App Purchases framework for iOS, tvOS, watchOS, macOS, and Mac Catalyst.\n\n## Features\n- Super easy-to-use block-based API\n- Support for consumable and non-consumable in-app purchases\n- Support for free, auto-renewable and non-renewing subscriptions\n- Support for in-app purchases started in the App Store (iOS 11)\n- Support for subscription discounts and offers\n- Remote receipt verification\n- Verify purchases, subscriptions, subscription groups\n- Downloading content hosted with Apple\n- iOS, tvOS, watchOS, macOS, and Catalyst compatible\n\n## SwiftyStoreKit Alternatives\n\nDuring WWDC21, Apple introduced [StoreKit 2](https://developer.apple.com/videos/play/wwdc2021/10114/), a brand new Swift API for in-app purchases and auto-renewable subscriptions. \n\nWhile it would be highly desirable to support StoreKit 2 in this project, [little progress](https://github.com/bizz84/SwiftyStoreKit/issues/550) has been made over the last year and most issues [remain unanswered](https://github.com/bizz84/SwiftyStoreKit/issues).\n\nFortunately, there are some very good alternatives to SwiftyStoreKit, backed by real companies. By choosing their products, you'll make a safe choice and get much better support.\n\n### RevenueCat\n\n[RevenueCat](https://www.revenuecat.com/) is a great alternative to SwiftyStoreKit, offering great APIs, support, and much more at a very [reasonable price](https://www.revenuecat.com/pricing).\n\nIf you've been using SwiftyStoreKit and want to migrate to RevenueCat, this guide covers everything you need:\n\n- [SwiftyStoreKit Migration](https://docs.revenuecat.com/docs/swiftystorekit)\n\nOr if you're just getting started, consider skipping SwiftyStoreKit altogether and signing up for [RevenueCat](https://www.revenuecat.com/).\n\n### Glassfy\n\n[Glassfy](https://glassfy.io/) makes it easy to build, handle, and optimize in-app subscriptions. If you switch to Glassfy from SwiftyStoreKit, you'll get a 20% discount by using this [affiliate link](https://dashboard.glassfy.io/referral?code=SWIFTYSTOREKIT20).\n\n- Glassfy [pricing page](https://glassfy.io/pricing.html) - 20% off\n- Glassfy [migration guide](https://docs.glassfy.io/get-started/migrate-ssk) to support you with the migration\n\n\u003e Note from the author: if you sign up with the link above, I will receive an affiliate commission from Glassfy, at no cost to yourself. I only recommend products that I personally know and believe will help you.\n\n### Apphud\n\n[Apphud](https://apphud.com/) is more than just making a purchase and validating receipts. Apphud is all-in-one infrastructure for your app growth. [Sign up for free](https://app.apphud.com/sign_up) and try it out.\n\nOr you can learn [how to migrate your app from SwiftyStoreKit to Apphud](https://docs.apphud.com/getting-started/migrate-from-swiftystorekit).\n\n### Adapty\n\nWith Adapty you can set up subscriptions in just an hour following these [simple steps](https://docs.adapty.io/docs/quickstart) and instantly launch in-app purchases with the [Paywall Builder](https://adapty.io/paywall-builder/). Adapty not only gives you the tools to embed purchases but also helps your customers grow.\nAnd the best part is that [it’s free for apps \u003c$10k](https://adapty.io/pricing/).\n\n## Contributions Wanted\nSwiftyStoreKit makes it easy for an incredible number of developers to seemlessly integrate in-App Purchases. This project, however, is now **community-led**. We need help building out features and writing tests (see [issue #550](https://github.com/bizz84/SwiftyStoreKit/issues/550)).\n\n### Maintainers Wanted\nThe author is no longer maintaining this project actively. If you'd like to become a maintainer, [join the Slack workspace](https://join.slack.com/t/swiftystorekit/shared_invite/enQtODY3OTYxOTExMzE5LWVkNGY4MzcwY2VjNGM4MGU4NDFhMGE5YmUxMGM3ZTQ4NjVjNTRkNTJhNDAyMWZmY2M5OWE5MDE0ODc3OGJjMmM) and enter the [#maintainers](https://app.slack.com/client/TL2JYQ458/CLG62K26A/details/) channel.\nGoing forward, SwiftyStoreKit should be made for the community, by the community. \n\nMore info here: [The Future of SwiftyStoreKit: Maintainers Wanted](https://medium.com/@biz84/the-future-of-swiftystorekit-maintainers-needed-f60d01572c91).\n\n## Requirements\nIf you've shipped an app in the last five years, you're probably good to go. Some features (like discounts) are only available on new OS versions, but most features are available as far back as:\n\n| iOS | watchOS | tvOS | macOS | Mac Catalyst |\n| --- | ------- | ---- | ----- | ------------ |\n| 8.0 | 6.2     | 9.0  | 10.10 | 13.0         |\n\n## Installation\nThere are a number of ways to install SwiftyStoreKit for your project. Swift Package Manager, CocoaPods, and Carthage integrations are the preferred and recommended approaches.\n\nRegardless, make sure to import the project wherever you may use it:\n\n```swift\nimport SwiftyStoreKit\n```\n\n### Swift Package Manager\nThe [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into Xcode and the Swift compiler. **This is the recommended installation method.** Updates to SwiftyStoreKit will always be available immediately to projects with SPM. SPM is also integrated directly with Xcode.\n\nIf you are using Xcode 11 or later:\n 1. Click `File`\n 2. `Swift Packages`\n 3. `Add Package Dependency...`\n 4. Specify the git URL for SwiftyStoreKit.\n\n```swift\nhttps://github.com/bizz84/SwiftyStoreKit.git\n```\n\n### Carthage\nTo integrate SwiftyStoreKit into your Xcode project using [Carthage](https://github.com/Carthage/Carthage), specify it in your Cartfile:\n\n```ogdl\ngithub \"bizz84/SwiftyStoreKit\"\n```\n\n**NOTE**: Please ensure that you have the [latest](https://github.com/Carthage/Carthage/releases) Carthage installed.\n\n### CocoaPods\nSwiftyStoreKit can be installed as a [CocoaPod](https://cocoapods.org/) and builds as a Swift framework. To install, include this in your Podfile.\n\n```ruby\nuse_frameworks!\n\npod 'SwiftyStoreKit'\n```\n\n## Contributing\nGot issues / pull requests / want to contribute? [Read here](CONTRIBUTING.md).\n\n# Documentation\nFull documentation is available on the [SwiftyStoreKit Wiki](https://github.com/bizz84/SwiftyStoreKit/wiki). As SwiftyStoreKit (and Apple's StoreKit) gains features, platforms, and implementation approaches, new information will be added to the Wiki. Essential documentation is available here in the README and should be enough to get you up and running.\n\n## App startup\n\n### Complete Transactions\n\nApple recommends to register a transaction observer [as soon as the app starts](https://developer.apple.com/library/ios/technotes/tn2387/_index.html):\n\u003e Adding your app's observer at launch ensures that it will persist during all launches of your app, thus allowing your app to receive all the payment queue notifications.\n\nSwiftyStoreKit supports this by calling `completeTransactions()` when the app starts:\n\n```swift\nfunc application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -\u003e Bool {\n\t// see notes below for the meaning of Atomic / Non-Atomic\n\tSwiftyStoreKit.completeTransactions(atomically: true) { purchases in\n\t    for purchase in purchases {\n\t        switch purchase.transaction.transactionState {\n\t        case .purchased, .restored:\n\t            if purchase.needsFinishTransaction {\n\t                // Deliver content from server, then:\n\t                SwiftyStoreKit.finishTransaction(purchase.transaction)\n\t            }\n\t            // Unlock content\n\t        case .failed, .purchasing, .deferred:\n\t            break // do nothing\n\t        }\n\t    }\n\t}\n    return true\n}\n```\n\nIf there are any pending transactions at this point, these will be reported by the completion block so that the app state and UI can be updated.\n\nIf there are no pending transactions, the completion block will **not** be called.\n\nNote that `completeTransactions()` **should only be called once** in your code, in `application(:didFinishLaunchingWithOptions:)`.\n\n## Purchases\n\n### Retrieve products info\n```swift\nSwiftyStoreKit.retrieveProductsInfo([\"com.musevisions.SwiftyStoreKit.Purchase1\"]) { result in\n    if let product = result.retrievedProducts.first {\n        let priceString = product.localizedPrice!\n        print(\"Product: \\(product.localizedDescription), price: \\(priceString)\")\n    }\n    else if let invalidProductId = result.invalidProductIDs.first {\n        print(\"Invalid product identifier: \\(invalidProductId)\")\n    }\n    else {\n        print(\"Error: \\(result.error)\")\n    }\n}\n```\n\n### Purchase a product (given a product id)\n\n* **Atomic**: to be used when the content is delivered immediately.\n\n```swift\nSwiftyStoreKit.purchaseProduct(\"com.musevisions.SwiftyStoreKit.Purchase1\", quantity: 1, atomically: true) { result in\n    switch result {\n    case .success(let purchase):\n        print(\"Purchase Success: \\(purchase.productId)\")\n    case .error(let error):\n        switch error.code {\n        case .unknown: print(\"Unknown error. Please contact support\")\n        case .clientInvalid: print(\"Not allowed to make the payment\")\n        case .paymentCancelled: break\n        case .paymentInvalid: print(\"The purchase identifier was invalid\")\n        case .paymentNotAllowed: print(\"The device is not allowed to make the payment\")\n        case .storeProductNotAvailable: print(\"The product is not available in the current storefront\")\n        case .cloudServicePermissionDenied: print(\"Access to cloud service information is not allowed\")\n        case .cloudServiceNetworkConnectionFailed: print(\"Could not connect to the network\")\n        case .cloudServiceRevoked: print(\"User has revoked permission to use this cloud service\")\n        default: print((error as NSError).localizedDescription)\n        }\n    }\n}\n```\n\n* **Non-Atomic**: to be used when the content is delivered by the server.\n\n```swift\nSwiftyStoreKit.purchaseProduct(\"com.musevisions.SwiftyStoreKit.Purchase1\", quantity: 1, atomically: false) { result in\n    switch result {\n    case .success(let product):\n        // fetch content from your server, then:\n        if product.needsFinishTransaction {\n            SwiftyStoreKit.finishTransaction(product.transaction)\n        }\n        print(\"Purchase Success: \\(product.productId)\")\n    case .error(let error):\n        switch error.code {\n        case .unknown: print(\"Unknown error. Please contact support\")\n        case .clientInvalid: print(\"Not allowed to make the payment\")\n        case .paymentCancelled: break\n        case .paymentInvalid: print(\"The purchase identifier was invalid\")\n        case .paymentNotAllowed: print(\"The device is not allowed to make the payment\")\n        case .storeProductNotAvailable: print(\"The product is not available in the current storefront\")\n        case .cloudServicePermissionDenied: print(\"Access to cloud service information is not allowed\")\n        case .cloudServiceNetworkConnectionFailed: print(\"Could not connect to the network\")\n        case .cloudServiceRevoked: print(\"User has revoked permission to use this cloud service\")\n        default: print((error as NSError).localizedDescription)\n        }\n    }\n}\n```\n\n### Additional Purchase Documentation\nThese additional topics are available on the Wiki:\n- [Purchase a product (given a SKProduct)](https://github.com/bizz84/SwiftyStoreKit/wiki/Purchasing#purchase-a-product-given-a-skproduct)\n- [Handle purchases started on the App Store (iOS 11)](https://github.com/bizz84/SwiftyStoreKit/wiki/App-Store-Purchases)\n\n### Restore Previous Purchases\n\nAccording to [Apple - Restoring Purchased Products](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html#//apple_ref/doc/uid/TP40008267-CH8-SW9):\n\n\u003e In most cases, all your app needs to do is refresh its receipt and deliver the products in its receipt. The refreshed receipt contains a record of the user’s purchases in this app, on this device or any other device.\n\u003e\n\u003e Restoring completed transactions creates a new transaction for every completed transaction the user made, essentially replaying history for your transaction queue observer.\n\nSee the **Receipt Verification** section below for how to restore previous purchases using the receipt.\n\nThis section shows how to restore completed transactions with the `restorePurchases` method instead. When successful, the method returns all non-consumable purchases, as well as all auto-renewable subscription purchases, **regardless of whether they are expired or not**.\n\n* **Atomic**: to be used when the content is delivered immediately.\n\n```swift\nSwiftyStoreKit.restorePurchases(atomically: true) { results in\n    if results.restoreFailedPurchases.count \u003e 0 {\n        print(\"Restore Failed: \\(results.restoreFailedPurchases)\")\n    }\n    else if results.restoredPurchases.count \u003e 0 {\n        print(\"Restore Success: \\(results.restoredPurchases)\")\n    }\n    else {\n        print(\"Nothing to Restore\")\n    }\n}\n```\n\n* **Non-Atomic**: to be used when the content is delivered by the server.\n\n```swift\nSwiftyStoreKit.restorePurchases(atomically: false) { results in\n    if results.restoreFailedPurchases.count \u003e 0 {\n        print(\"Restore Failed: \\(results.restoreFailedPurchases)\")\n    }\n    else if results.restoredPurchases.count \u003e 0 {\n        for purchase in results.restoredPurchases {\n            // fetch content from your server, then:\n            if purchase.needsFinishTransaction {\n                SwiftyStoreKit.finishTransaction(purchase.transaction)\n            }\n        }\n        print(\"Restore Success: \\(results.restoredPurchases)\")\n    }\n    else {\n        print(\"Nothing to Restore\")\n    }\n}\n```\n\n#### What does atomic / non-atomic mean?\nFor more information about atomic vs. non-atomic restorations, [view the Wiki page here](https://github.com/bizz84/SwiftyStoreKit/wiki/Restoring#what-does-atomic--non-atomic-mean).\n\n### Downloading content hosted with Apple\nMore information about downloading hosted content is [available on the Wiki](https://github.com/bizz84/SwiftyStoreKit/wiki/Downloading-Content).\n\nTo start downloads (this can be done in `purchaseProduct()`, `completeTransactions()` or `restorePurchases()`):\n\n```swift\nSwiftyStoreKit.purchaseProduct(\"com.musevisions.SwiftyStoreKit.Purchase1\", quantity: 1, atomically: false) { result in\n    switch result {\n    case .success(let product):\n        let downloads = purchase.transaction.downloads\n        if !downloads.isEmpty {\n            SwiftyStoreKit.start(downloads)\n        }\n    case .error(let error):\n        print(\"\\(error)\")\n    }\n}\n```\n\nTo check the updated downloads, setup a `updatedDownloadsHandler` block in your AppDelegate:\n\n```swift\nSwiftyStoreKit.updatedDownloadsHandler = { downloads in\n    // contentURL is not nil if downloadState == .finished\n    let contentURLs = downloads.flatMap { $0.contentURL }\n    if contentURLs.count == downloads.count {\n        // process all downloaded files, then finish the transaction\n        SwiftyStoreKit.finishTransaction(downloads[0].transaction)\n    }\n}\n```\n\nTo control the state of the downloads, SwiftyStoreKit offers `start()`, `pause()`, `resume()`, `cancel()` methods.\n\n## Receipt verification\n\nThis helper can be used to retrieve the (encrypted) local receipt data:\n\n```swift\nlet receiptData = SwiftyStoreKit.localReceiptData\nlet receiptString = receiptData.base64EncodedString(options: [])\n// do your receipt validation here\n```\n\nHowever, the receipt file may be missing or outdated. Use this method to get the updated receipt:\n\n```swift\nSwiftyStoreKit.fetchReceipt(forceRefresh: true) { result in\n    switch result {\n    case .success(let receiptData):\n        let encryptedReceipt = receiptData.base64EncodedString(options: [])\n        print(\"Fetch receipt success:\\n\\(encryptedReceipt)\")\n    case .error(let error):\n        print(\"Fetch receipt failed: \\(error)\")\n    }\n}\n```\n\nUse this method to (optionally) refresh the receipt and perform validation in one step.\n\n```swift\nlet appleValidator = AppleReceiptValidator(service: .production, sharedSecret: \"your-shared-secret\")\nSwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in\n    switch result {\n    case .success(let receipt):\n        print(\"Verify receipt success: \\(receipt)\")\n    case .error(let error):\n        print(\"Verify receipt failed: \\(error)\")\n    }\n}\n```\n\nAdditional details about receipt verification are [available on the wiki](https://github.com/bizz84/SwiftyStoreKit/wiki/Verify-Receipt).\n\n## Verifying purchases and subscriptions\nOnce you have retrieved the receipt using the `verifyReceipt` method, you can verify your purchases and subscriptions by product identifier.\n\n### Verify Purchase\n\n```swift\nlet appleValidator = AppleReceiptValidator(service: .production, sharedSecret: \"your-shared-secret\")\nSwiftyStoreKit.verifyReceipt(using: appleValidator) { result in\n    switch result {\n    case .success(let receipt):\n        let productId = \"com.musevisions.SwiftyStoreKit.Purchase1\"\n        // Verify the purchase of Consumable or NonConsumable\n        let purchaseResult = SwiftyStoreKit.verifyPurchase(\n            productId: productId,\n            inReceipt: receipt)\n            \n        switch purchaseResult {\n        case .purchased(let receiptItem):\n            print(\"\\(productId) is purchased: \\(receiptItem)\")\n        case .notPurchased:\n            print(\"The user has never purchased \\(productId)\")\n        }\n    case .error(let error):\n        print(\"Receipt verification failed: \\(error)\")\n    }\n}\n```\n\n### Verify Subscription\nThis can be used to check if a subscription was previously purchased, and whether it is still active or if it's expired.\n\nFrom [Apple - Working with Subscriptions](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html#//apple_ref/doc/uid/TP40008267-CH7-SW6):\n\n\u003e Keep a record of the date that each piece of content is published. Read the Original Purchase Date and Subscription Expiration Date field from each receipt entry to determine the start and end dates of the subscription.\n\nWhen one or more subscriptions are found for a given product id, they are returned as a `ReceiptItem` array ordered by `expiryDate`, with the first one being the newest.\n\n```swift\nlet appleValidator = AppleReceiptValidator(service: .production, sharedSecret: \"your-shared-secret\")\nSwiftyStoreKit.verifyReceipt(using: appleValidator) { result in\n    switch result {\n    case .success(let receipt):\n        let productId = \"com.musevisions.SwiftyStoreKit.Subscription\"\n        // Verify the purchase of a Subscription\n        let purchaseResult = SwiftyStoreKit.verifySubscription(\n            ofType: .autoRenewable, // or .nonRenewing (see below)\n            productId: productId,\n            inReceipt: receipt)\n            \n        switch purchaseResult {\n        case .purchased(let expiryDate, let items):\n            print(\"\\(productId) is valid until \\(expiryDate)\\n\\(items)\\n\")\n        case .expired(let expiryDate, let items):\n            print(\"\\(productId) is expired since \\(expiryDate)\\n\\(items)\\n\")\n        case .notPurchased:\n            print(\"The user has never purchased \\(productId)\")\n        }\n\n    case .error(let error):\n        print(\"Receipt verification failed: \\(error)\")\n    }\n}\n```\n\nFurther documentation on verifying subscriptions is [available on the wiki](https://github.com/bizz84/SwiftyStoreKit/wiki/Verify-Subscription).\n\n### Subscription Groups\n\nFrom [Apple Docs - Offering Subscriptions](https://developer.apple.com/app-store/subscriptions/):\n\n\u003e A subscription group is a set of in-app purchases that you can create to provide users with a range of content offerings, service levels, or durations to best meet their needs. Users can only buy one subscription within a subscription group at a time. If users would want to buy more that one type of subscription — for example, to subscribe to more than one channel in a streaming app — you can put these in-app purchases in different subscription groups.\n\nYou can verify all subscriptions within the same group with the `verifySubscriptions` method. [Learn more on the wiki](https://github.com/bizz84/SwiftyStoreKit/wiki/Subscription-Groups).\n\n\n## Notes\nThe framework provides a simple block based API with robust error handling on top of the existing StoreKit framework. It does **NOT** persist in app purchases data locally. It is up to clients to do this with a storage solution of choice (i.e. NSUserDefaults, CoreData, Keychain).\n\n## Change Log\nSee the [Releases Page](https://github.com/bizz84/SwiftyStoreKit/releases).\n\n## Sample Code\nThe project includes demo apps [for iOS](https://github.com/bizz84/SwiftyStoreKit/blob/master/SwiftyStoreKit-iOS-Demo/ViewController.swift) [and macOS](https://github.com/bizz84/SwiftyStoreKit/blob/master/SwiftyStoreKit-macOS-Demo/ViewController.swift) showing how to use SwiftyStoreKit.\nNote that the pre-registered in app purchases in the demo apps are for illustration purposes only and may not work as iTunes Connect may invalidate them.\n\n## Essential Reading\n* [Apple - WWDC16, Session 702: Using Store Kit for In-app Purchases with Swift 3](https://developer.apple.com/videos/play/wwdc2016/702/)\n* [Apple - TN2387: In-App Purchase Best Practices](https://developer.apple.com/library/content/technotes/tn2387/_index.html)\n* [Apple - TN2413: In-App Purchase FAQ](https://developer.apple.com/library/content/technotes/tn2413/_index.html) (also see [Cannot connect to iTunes Store](https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-ERROR_MESSAGES-CANNOT_CONNECT_TO_ITUNES_STORE))\n* [Apple - TN2259: Adding In-App Purchase to Your Applications](https://developer.apple.com/library/content/technotes/tn2259/_index.html)\n* [iTunes Connect Developer Help - Workflow for configuring in-app purchases](https://help.apple.com/itunes-connect/developer/#/devb57be10e7)\n* [Apple - About Receipt Validation](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Introduction.html)\n* [Apple - Receipt Validation Programming Guide](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1)\n* [Apple - Validating Receipts Locally](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html)\n* [Apple - Working with Subscriptions](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html#//apple_ref/doc/uid/TP40008267-CH7-SW6)\n* [Apple - Offering Subscriptions](https://developer.apple.com/app-store/subscriptions/)\n* [Apple - Restoring Purchased Products](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html#//apple_ref/doc/uid/TP40008267-CH8-SW9)\n* [Apple - Testing In-App Purchase Products](https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/TestingInAppPurchases.html): includes info on duration of subscriptions in sandbox mode\n* [objc.io - Receipt Validation](https://www.objc.io/issues/17-security/receipt-validation/)\n\nI have also written about building SwiftyStoreKit on Medium:\n\n* [How I got 1000 ⭐️ on my GitHub Project](https://medium.com/ios-os-x-development/how-i-got-1000-%EF%B8%8F-on-my-github-project-654d3d394ca6#.1idp27olf)\n* [Maintaining a Growing Open Source Project](https://medium.com/@biz84/maintaining-a-growing-open-source-project-1d385ca84c5#.4cv2g7tdc)\n\n### Troubleshooting \n* [Apple TN 2413 - Why are my product identifiers being returned in the invalidProductIdentifiers array?](https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-TROUBLESHOOTING-WHY_ARE_MY_PRODUCT_IDENTIFIERS_BEING_RETURNED_IN_THE_INVALIDPRODUCTIDENTIFIERS_ARRAY_)\n* [Invalid Product IDs](http://troybrant.net/blog/2010/01/invalid-product-ids/): Checklist of common mistakes\n* [Testing Auto-Renewable Subscriptions on iOS](http://davidbarnard.com/post/164337147440/testing-auto-renewable-subscriptions-on-ios)\n* [Apple forums - iOS 11 beta sandbox - cannot connect to App Store](https://forums.developer.apple.com/message/261428#261428)\n\n## Video Tutorials\n\n#### Jared Davidson: In App Purchases! (Swift 3 in Xcode : Swifty Store Kit)\n\n\u003ca href=\"https://www.youtube.com/watch?v=dwPFtwDJ7tcb\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/bizz84/SwiftyStoreKit/master/Screenshots/VideoTutorial-JaredDavidson.jpg\" width=\"50%\" /\u003e\u003c/a\u003e\n\n#### [@rebeloper](https://github.com/rebeloper): Ultimate In-app Purchases Guide\n\n\u003ca href=\"https://www.youtube.com/watch?v=MP-U5gQylHc\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/2488011/65576278-55cccc80-df7a-11e9-8db5-244e2afa3e46.png\" width=\"50%\" /\u003e\u003c/a\u003e\n\n## Written Tutorials\n\n* [In App Purchase (IAP) made simple with SwiftyStoreKit](https://levelup.gitconnected.com/beginner-ios-dev-in-app-purchase-iap-made-simple-with-swiftystorekit-3add60e9065d)\n\n## Credits\nMany thanks to [phimage](https://github.com/phimage) for adding macOS support and receipt verification.\n\n## Apps using SwiftyStoreKit\n\nIt would be great to showcase apps using SwiftyStoreKit here. Pull requests welcome :)\n\n* [Every Plant, Ever](https://itunes.apple.com/us/app/every-plant-ever/id1433967019) - The sticker pack of every plant, ever.\n* [Countdown](https://countdowns.download/ssk) - Countdown the days until your next vacation, deadline, or event\n* [MDacne](https://itunes.apple.com/app/id1044050208) - Acne analysis and treatment\n* [Pixel Picker](https://itunes.apple.com/app/id930804327) - Image Color Picker\n* [KType](https://itunes.apple.com/app/id1037000234) - Space shooter game\n* [iPic](https://itunes.apple.com/app/id1101244278) - Automatically upload images and save Markdown links\n* [iHosts](https://itunes.apple.com/app/id1102004240) - Perfect for editing /etc/hosts\n* [Arise](http://www.abnehm-app.de/) - Calorie counter\n* [Truth Truth Lie](https://itunes.apple.com/app/id1130832864) - iMessage game, featured by Apple\n* [Tactus Music Player](https://itunes.apple.com/app/id557446352) - Alternative music player app\n* [Drops](https://itunes.apple.com/app/id939540371) - Language learning app\n* [Fresh Snow](https://itunes.apple.com/app/id1063000470) - Colorado Ski Report\n* [Zmeu Grand Canyon](http://grandcanyon.zmeu.guide/) - Interactive hiking map \u0026 planner\n* [OB Monitor](https://itunes.apple.com/app/id1073398446) - The app for Texas Longhorns athletics fans\n* [Talk Dim Sum](https://itunes.apple.com/us/app/talk-dim-sum/id953929066) - Your dim sum companion\n* [Sluggard](https://itunes.apple.com/app/id1160131071) - Perform simple exercises to reduce the risks of sedentary lifestyle\n* [Debts iOS](https://debts.ivanvorobei.by/ios) \u0026 [Debts macOS](https://debts.ivanvorobei.by/macos) - Track amounts owed\n* [Botcher](https://itunes.apple.com/us/app/id1522337788) - Good for finding something to do\n* [Hashr](https://apps.apple.com/app/id1166499829) - Generate unique password hashes based on website and master password\n* [QRFi](https://apps.apple.com/app/id1535761355) - Create stylish QR Codes for WiFi\n\nA full list of apps is published [on AppSight](https://www.appsight.io/sdk/574154).\n","funding_links":[],"categories":["Payments","Libs","Swift","HarmonyOS","\u003ca name=\"for-projects\"\u003e\u003c/a\u003e For Projects","App Store [🔝](#readme)","OOM-Leaks-Crash","Minor","In App Purchases and Subscription"],"sub_categories":["Other Parsing","App Store","Other free courses","Windows Manager","\u003ca name=\"for-projects-example-projects\"\u003e\u003c/a\u003e Example Projects","Payment","IAP"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbizz84%2FSwiftyStoreKit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbizz84%2FSwiftyStoreKit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbizz84%2FSwiftyStoreKit/lists"}