在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):bizz84/SwiftyStoreKit开源软件地址(OpenSource Url):https://github.com/bizz84/SwiftyStoreKit开源编程语言(OpenSource Language):Swift 95.3%开源软件介绍(OpenSource Introduction):SwiftyStoreKit is a lightweight In App Purchases framework for iOS, tvOS, watchOS, macOS, and Mac Catalyst. Features
SwiftyStoreKit AlternativesDuring WWDC21, Apple introduced StoreKit 2, a brand new Swift API for in-app purchases and auto-renewable subscriptions. While it would be highly desirable to support StoreKit 2 in this project, little progress has been made over the last year and most issues remain unanswered. Fortunately, 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. RevenueCatRevenueCat is a great alternative to SwiftyStoreKit, offering great APIs, support, and much more at a very reasonable price. If you've been using SwiftyStoreKit and want to migrate to RevenueCat, this guide covers everything you need: Or if you're just getting started, consider skipping SwiftyStoreKit altogether and signing up for RevenueCat. GlassfyGlassfy 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.
Contributions WantedSwiftyStoreKit 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). Maintainers WantedThe author is no longer maintaining this project actively. If you'd like to become a maintainer, join the Slack workspace and enter the #maintainers channel. Going forward, SwiftyStoreKit should be made for the community, by the community. More info here: The Future of SwiftyStoreKit: Maintainers Wanted. RequirementsIf 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:
InstallationThere are a number of ways to install SwiftyStoreKit for your project. Swift Package Manager, CocoaPods, and Carthage integrations are the preferred and recommended approaches. Regardless, make sure to import the project wherever you may use it: import SwiftyStoreKit Swift Package ManagerThe Swift 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. If you are using Xcode 11 or later:
https://github.com/bizz84/SwiftyStoreKit.git CarthageTo integrate SwiftyStoreKit into your Xcode project using Carthage, specify it in your Cartfile:
NOTE: Please ensure that you have the latest Carthage installed. CocoaPodsSwiftyStoreKit can be installed as a CocoaPod and builds as a Swift framework. To install, include this in your Podfile. use_frameworks!
pod 'SwiftyStoreKit' ContributingGot issues / pull requests / want to contribute? Read here. DocumentationFull documentation is available on the 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. App startupComplete TransactionsApple recommends to register a transaction observer as soon as the app starts:
SwiftyStoreKit supports this by calling 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
} If 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. If there are no pending transactions, the completion block will not be called. Note that PurchasesRetrieve products infoSwiftyStoreKit.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)")
}
} Purchase a product (given a product id)
SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: true) { result in
switch result {
case .success(let purchase):
print("Purchase Success: \(purchase.productId)")
case .error(let error):
switch error.code {
case .unknown: print("Unknown error. Please contact support")
case .clientInvalid: print("Not allowed to make the payment")
case .paymentCancelled: break
case .paymentInvalid: print("The purchase identifier was invalid")
case .paymentNotAllowed: print("The device is not allowed to make the payment")
case .storeProductNotAvailable: print("The product is not available in the current storefront")
case .cloudServicePermissionDenied: print("Access to cloud service information is not allowed")
case .cloudServiceNetworkConnectionFailed: print("Could not connect to the network")
case .cloudServiceRevoked: print("User has revoked permission to use this cloud service")
default: print((error as NSError).localizedDescription)
}
}
}
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):
switch error.code {
case .unknown: print("Unknown error. Please contact support")
case .clientInvalid: print("Not allowed to make the payment")
case .paymentCancelled: break
case .paymentInvalid: print("The purchase identifier was invalid")
case .paymentNotAllowed: print("The device is not allowed to make the payment")
case .storeProductNotAvailable: print("The product is not available in the current storefront")
case .cloudServicePermissionDenied: print("Access to cloud service information is not allowed")
case .cloudServiceNetworkConnectionFailed: print("Could not connect to the network")
case .cloudServiceRevoked: print("User has revoked permission to use this cloud service")
default: print((error as NSError).localizedDescription)
}
}
} Additional Purchase DocumentationThese additional topics are available on the Wiki: Restore Previous PurchasesAccording to Apple - Restoring Purchased Products:
See the Receipt Verification section below for how to restore previous purchases using the receipt. This section shows how to restore completed transactions with the
SwiftyStoreKit.restorePurchases(atomically: true) { results in
if results.restoreFailedPurchases.count > 0 {
print("Restore Failed: \(results.restoreFailedPurchases)")
}
else if results.restoredPurchases.count > 0 {
print("Restore Success: \(results.restoredPurchases)")
}
else {
print("Nothing to Restore")
}
}
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")
}
} What does atomic / non-atomic mean?For more information about atomic vs. non-atomic restorations, view the Wiki page here. Downloading content hosted with AppleMore information about downloading hosted content is available on the Wiki. To start downloads (this can be done in SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: false) { result in
switch result {
case .success(let product):
let downloads = purchase.transaction.downloads
if !downloads.isEmpty {
SwiftyStoreKit.start(downloads)
}
case .error(let error):
print("\(error)")
}
} To check the updated downloads, setup a SwiftyStoreKit.updatedDownloadsHandler = { downloads in
// contentURL is not nil if downloadState == .finished
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 control the state of the downloads, SwiftyStoreKit offers Receipt verificationThis helper can be used to retrieve the (encrypted) local receipt data: let receiptData = SwiftyStoreKit.localReceiptData
let receiptString = receiptData.base64EncodedString(options: [])
// do your receipt validation here However, the receipt file may be missing or outdated. Use this method to get the updated receipt: SwiftyStoreKit.fetchReceipt(forceRefresh: true) { result in
switch result {
case .success(let receiptData):
let encryptedReceipt = receiptData.base64EncodedString(options: [])
print("Fetch receipt success:\n\(encryptedReceipt)")
case .error(let error):
print("Fetch receipt failed: \(error)")
}
} Use this method to (optionally) refresh the receipt and perform validation in one step. 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)")
}
} Additional details about receipt verification are available on the wiki. Verifying purchases and subscriptionsOnce you have retrieved the receipt using the Verify Purchaselet 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)")
}
} Verify SubscriptionThis can be used to check if a subscription was previously purchased, and whether it is still active or if it's expired. From Apple - Working with Subscriptions:
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论