Install SwiftyStoreKit via Carthage
masterTo integrate SwiftyStoreKit using Carthage, add the following line to your Cartfile. Ensure you have the latest version of Carthage installed.
github "bizz84/SwiftyStoreKit"repository·master·Indexed 27 days ago
https://github.com/bizz84/swiftystorekitA lightweight In-App Purchases framework for Apple platforms (iOS, tvOS, watchOS, macOS, and Mac Catalyst). It provides a block-based API for managing consumables, non-consumables, and subscriptions, including features for retrieving product information, handling pending transactions, restoring purchases, downloading Apple-hosted content, and verifying receipts and subscription statuses.
To integrate SwiftyStoreKit using Carthage, add the following line to your Cartfile. Ensure you have the latest version of Carthage installed.
github "bizz84/SwiftyStoreKit"Swift Package Manager (SPM) is the recommended installation method. If you are using Xcode 11 or later, follow these steps:
File in the menu bar.Swift Packages.Add Package Dependency....https://github.com/bizz84/SwiftyStoreKit.gitTo handle content hosted by Apple, use the downloads property on a transaction.
SwiftyStoreKit.start(downloads) within a purchase or restore completion block.SwiftyStoreKit.updatedDownloadsHandler in your AppDelegate.contentURL is available (indicating .finished state), process the files and call SwiftyStoreKit.finishTransaction(downloads[0].transaction).Control methods: start(), pause(), resume(), cancel().
// 1. Start downloads
SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: false) { result in
switch result {
case .success(let product):
let downloads = product.transaction.downloads
if !downloads.isEmpty {
SwiftyStoreKit.start(downloads)
}
case .error(let error):
print("\(error)")
}
}
// 2. Monitor in AppDelegate
SwiftyStoreKit.updatedDownloadsHandler = { downloads in
let contentURLs = downloads.flatMap { $0.contentURL }
if contentURLs.count == downloads.count {
// process all downloaded files, then finish the transaction
SwiftyStoreKit.finishTransaction(downloads[0].transaction)
}
}To install SwiftyStoreKit as a CocoaPod, include the following in your Podfile. Note that it builds as a Swift framework, so use_frameworks! is required.
use_frameworks!
pod 'SwiftyStoreKit'Apple recommends registering a transaction observer as soon as the app starts. SwiftyStoreKit provides completeTransactions(atomically:completion:) for this purpose.
Important:
completeTransactions() only once in your application lifecycle, typically within application(_:didFinishLaunchingWithOptions:).transactionState. If it is .purchased or .restored, deliver the content and then call SwiftyStoreKit.finishTransaction(_:) if purchase.needsFinishTransaction is true.import SwiftyStoreKit
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// see notes below for the meaning of Atomic / Non-Atomic
SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
for purchase in purchases {
switch purchase.transaction.transactionState {
case .purchased, .restored:
if purchase.needsFinishTransaction {
// Deliver content from server, then:
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
// Unlock content
case .failed, .purchasing, .deferred:
break // do nothing
}
}
}
return true
}Use SwiftyStoreKit.restorePurchases to restore non-consumable and auto-renewable subscription purchases.
atomically: true for immediate content delivery.atomically: false for server-side delivery. If using non-atomic, iterate through results.restoredPurchases and call SwiftyStoreKit.finishTransaction(purchase.transaction) if purchase.needsFinishTransaction is true.Results include restoredPurchases and restoreFailedPurchases.
SwiftyStoreKit.restorePurchases(atomically: false) { results in
if results.restoreFailedPurchases.count > 0 {
print("Restore Failed: \(results.restoreFailedPurchases)")
}
else if results.restoredPurchases.count > 0 {
for purchase in results.restoredPurchases {
// fetch content from your server, then:
if purchase.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
}
print("Restore Success: \(results.restoredPurchases)")
}
else {
print("Nothing to Restore")
}
}Use SwiftyStoreKit.retrieveProductsInfo to fetch metadata for a list of product identifiers. The result contains retrievedProducts (successful lookups) and invalidProductIDs (IDs that could not be found in the storefront).
SwiftyStoreKit.retrieveProductsInfo(["com.musevisions.SwiftyStoreKit.Purchase1"]) { result in
if let product = result.retrievedProducts.first {
let priceString = product.localizedPrice!
print("Product: \(product.localizedDescription), price: \(priceString)")
}
else if let invalidProductId = result.invalidProductIDs.first {
print("Invalid product identifier: \(invalidProductId)")
}
else {
print("Error: \(result.error)")
}
}SwiftyStoreKit provides several ways to handle receipt verification:
SwiftyStoreKit.localReceiptData for the current encrypted receipt.SwiftyStoreKit.fetchReceipt(forceRefresh: true) to ensure the receipt is up-to-date before validation.SwiftyStoreKit.verifyReceipt(using:forceRefresh:) with an AppleReceiptValidator to perform a complete validation against Apple's servers in one step.// Full verification example
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in
switch result {
case .success(let receipt):
print("Verify receipt success: \(receipt)")
case .error(let error):
print("Verify receipt failed: \(error)")
}
}Use SwiftyStoreKit.purchaseProduct to initiate a purchase.
atomically: true when content is delivered immediately.atomically: false when content is delivered by a server. In this mode, you must check product.needsFinishTransaction and call SwiftyStoreKit.finishTransaction(product.transaction) after your server-side logic is complete.Common error codes include .paymentCancelled, .paymentInvalid, .storeProductNotAvailable, and various .cloudService errors.
// Non-Atomic Example (Server-side delivery)
SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: false) { result in
switch result {
case .success(let product):
// fetch content from your server, then:
if product.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(product.transaction)
}
print("Purchase Success: \(product.productId)")
case .error(let error):
// handle error
break
}
}Use verifySubscription(ofType:productId:inReceipt:) to check if a subscription is active, expired, or never purchased. The ofType parameter accepts .autoRenewable or .nonRenewing. When a subscription is found, it returns a ReceiptItem array ordered by expiryDate, with the newest item first.
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
switch result {
case .success(let receipt):
let productId = "com.musevisions.SwiftyStoreKit.Subscription"
// Verify the purchase of a Subscription
let purchaseResult = SwiftyStoreKit.verifySubscription(
ofType: .autoRenewable, // or .nonRenewing (see below)
productId: productId,
inReceipt: receipt)
switch purchaseResult {
case .purchased(let expiryDate, let items):
print("\(productId) is valid until \(expiryDate)\n\(items)\n")
case .expired(let expiryDate, let items):
print("\(productId) is expired since \(expiryDate)\n\(items)\n")
case .notPurchased:
print("The user has never purchased \(productId)")
}
case .error(let error):
print("Receipt verification failed: \(error)")
}
}To verify a specific product purchase, first retrieve the receipt using verifyReceipt with an AppleReceiptValidator. Once the receipt is obtained, use verifyPurchase(productId:inReceipt:) to check the status of a specific product identifier.
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
switch result {
case .success(let receipt):
let productId = "com.musevisions.SwiftyStoreKit.Purchase1"
// Verify the purchase of Consumable or NonConsumable
let purchaseResult = SwiftyStoreKit.verifyPurchase(
productId: productId,
inReceipt: receipt)
switch purchaseResult {
case .purchased(let receiptItem):
print("\(productId) is purchased: \(receiptItem)")
case .notPurchased:
print("The user has never purchased \(productId)")
}
case .error(let error):
print("Receipt verification failed: \(error)")
}
}verifySubscriptions method.