如何在iOS应用中添加应用内购买功能?

如何在iOS应用中添加应用内购买功能?所有的细节是什么,是否有任何示例代码?

这是关于如何在iOS应用中添加应用内购买的各种方法

116528 次浏览

快速用户

Swift用户可以签出我对这个问题的快速回答。或者,查看Yedidya Reiss的回答,它将这段Objective-C代码转换为Swift。

objective - c用户

剩下的答案是用Objective-C编写的

App Store Connect

  1. 进入appstoreconnect.apple.com并登录
  2. 单击My Apps,然后单击要添加购买的应用程序
  3. 单击Features标头,然后选择左边的In-App Purchases
  4. 单击中间的+图标
  5. 在本教程中,我们将添加应用内购买来删除广告,因此选择non-consumable。如果你要向用户发送一个实体物品,或者给他们可以多次购买的东西,你会选择consumable
  6. 对于引用名称,可以输入您想要的任何名称(但要确保您知道它是什么)
  7. 对于产品id,请输入tld.websitename.appname.referencename,这将是最好的,因此,例如,您可以使用com.jojodmo.blix.removeads
  8. 选择cleared for sale,然后选择价格层为1(99美分)。第2层是1.99美元,第3层是2.99美元。完整的列表是可用的,如果你点击view pricing matrix。我建议你使用第1层,因为这通常是最多的人会支付删除广告。
  9. 单击蓝色的add language按钮,并输入信息。这些都将展示给客户,所以不要放任何你不想让他们看到的东西
  10. 对于hosting content with Apple,选择没有
  11. 你可以把复习笔记留空现在
  12. 跳过screenshot for review 现在,所有我们跳过的都会回来。
  13. 点击“保存”

你的产品ID在App Store Connect中注册可能需要几个小时,所以要有耐心。

设置项目

现在你已经在App Store Connect上设置了你的应用内购买信息,进入你的Xcode项目,进入应用管理器(方法和头文件顶部的蓝色页面状图标),在目标下点击你的应用(应该是第一个),然后进入general。在底部,你应该看到linked frameworks and libraries点击小的加号,并添加框架StoreKit.framework如果你不这样做,应用内购买将工作!

如果你使用Objective-C作为你的应用程序的语言,你应该跳过这五个步骤吗。否则,如果你正在使用Swift,你可以遵循我对这个问题的快速回答,在这里,或者,如果你更喜欢使用Objective-C的应用程序内购买代码,但在你的应用程序中使用Swift,你可以做以下事情:

  1. 通过File >New祝辞__abc3 (__abc5 + __abc6)。此文件将被称为“您的.h文件”;在本教程的其余部分

  2. 当出现提示时,单击创建桥接头。这将是桥接头文件。如果提示,则转到步骤3。如果你提示,跳过步骤3,直接进入步骤4。

  3. 在主项目文件夹中创建另一个名为Bridge.h.h文件,然后转到应用程序管理器(蓝色页面状图标),然后在Targets部分选择您的应用程序,并单击Build Settings。找到Swift编译器-代码生成的选项,然后将Objective-C桥接头选项设置为Bridge.h

  4. 在桥接头文件中,添加行#import "MyObjectiveCHeaderFile.h",其中MyObjectiveCHeaderFile是你在第一步中创建的头文件的名称。因此,例如,如果您将头文件命名为InAppPurchase.h,那么您将在桥接头文件中添加#import "InAppPurchase.h"行。

  5. 通过File >创建一个新的Objective-C Methods (.m)文件;New祝辞__abc3 (__abc5 + __abc6)。将其命名为与步骤1中创建的头文件相同的名称。例如,如果你将步骤1中的文件称为InAppPurchase.h,那么你将把这个新文件称为InAppPurchase.m。此文件将被称为“您的.m文件”;在本教程的其余部分。

编码

现在我们要进入实际的编码。将以下代码添加到你的.h文件中:

BOOL areAdsRemoved;


- (IBAction)restore;
- (IBAction)tapsRemoveAds;

接下来,你需要将StoreKit框架导入到你的.m文件中,并在你的@interface声明后添加SKProductsRequestDelegateSKPaymentTransactionObserver:

#import <StoreKit/StoreKit.h>


//put the name of your view controller in place of MyViewController
@interface MyViewController() <SKProductsRequestDelegate, SKPaymentTransactionObserver>


@end


@implementation MyViewController //the name of your view controller (same as above)
//the code below will be added here
@end

现在将以下内容添加到你的.m文件中,这部分会变得复杂,所以我建议你阅读代码中的注释:

//If you have more than one in-app purchase, you can define both of
//of them here. So, for example, you could define both kRemoveAdsProductIdentifier
//and kBuyCurrencyProductIdentifier with their respective product ids
//
//for this example, we will only use one product


#define kRemoveAdsProductIdentifier @"put your product id (the one that we just made in App Store Connect) in here"


- (IBAction)tapsRemoveAds{
NSLog(@"User requests to remove ads");


if([SKPaymentQueue canMakePayments]){
NSLog(@"User can make payments");
    

//If you have more than one in-app purchase, and would like
//to have the user purchase a different product, simply define
//another function and replace kRemoveAdsProductIdentifier with
//the identifier for the other product


SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:kRemoveAdsProductIdentifier]];
productsRequest.delegate = self;
[productsRequest start];
    

}
else{
NSLog(@"User cannot make payments due to parental controls");
//this is called the user cannot make payments, most likely due to parental controls
}
}


- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
SKProduct *validProduct = nil;
int count = [response.products count];
if(count > 0){
validProduct = [response.products objectAtIndex:0];
NSLog(@"Products Available!");
[self purchase:validProduct];
}
else if(!validProduct){
NSLog(@"No products available");
//this is called if your product id is not valid, this shouldn't be called unless that happens.
}
}


- (void)purchase:(SKProduct *)product{
SKPayment *payment = [SKPayment paymentWithProduct:product];


[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}


- (IBAction) restore{
//this is called when the user restores purchases, you should hook this up to a button
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}


- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(@"received restored transactions: %i", queue.transactions.count);
for(SKPaymentTransaction *transaction in queue.transactions){
if(transaction.transactionState == SKPaymentTransactionStateRestored){
//called when the user successfully restores a purchase
NSLog(@"Transaction state -> Restored");


//if you have more than one in-app purchase product,
//you restore the correct product for the identifier.
//For example, you could use
//if(productID == kRemoveAdsProductIdentifier)
//to get the product identifier for the
//restored purchases, you can use
//
//NSString *productID = transaction.payment.productIdentifier;
[self doRemoveAds];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}


- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for(SKPaymentTransaction *transaction in transactions){
//if you have multiple in app purchases in your app,
//you can get the product identifier of this transaction
//by using transaction.payment.productIdentifier
//
//then, check the identifier against the product IDs
//that you have defined to check which product the user
//just purchased


switch(transaction.transactionState){
case SKPaymentTransactionStatePurchasing: NSLog(@"Transaction state -> Purchasing");
//called when the user is in the process of purchasing, do not add any of your own code here.
break;
case SKPaymentTransactionStatePurchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
[self doRemoveAds]; //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(@"Transaction state -> Purchased");
break;
case SKPaymentTransactionStateRestored:
NSLog(@"Transaction state -> Restored");
//add the same code as you did from SKPaymentTransactionStatePurchased here
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
//called when the transaction does not finish
if(transaction.error.code == SKErrorPaymentCancelled){
NSLog(@"Transaction state -> Cancelled");
//the user cancelled the payment ;(
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}

现在你想为用户完成交易时发生的事情添加你的代码,在本教程中,我们使用删除添加,你必须为横幅视图加载时发生的事情添加你自己的代码。

- (void)doRemoveAds{
ADBannerView *banner;
[banner setAlpha:0];
areAdsRemoved = YES;
removeAdsButton.hidden = YES;
removeAdsButton.enabled = NO;
[[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"];
//use NSUserDefaults so that you can load whether or not they bought it
//it would be better to use KeyChain access, or something more secure
//to store the user data, because NSUserDefaults can be changed.
//You're average downloader won't be able to change it very easily, but
//it's still best to use something more secure than NSUserDefaults.
//For the purpose of this tutorial, though, we're going to use NSUserDefaults
[[NSUserDefaults standardUserDefaults] synchronize];
}

如果你的应用程序中没有广告,你可以使用任何你想要的东西。例如,我们可以将背景颜色设置为蓝色。要做到这一点,我们需要使用:

- (void)doRemoveAds{
[self.view setBackgroundColor:[UIColor blueColor]];
areAdsRemoved = YES
//set the bool for whether or not they purchased it to YES, you could use your own boolean here, but you would have to declare it in your .h file


[[NSUserDefaults standardUserDefaults] setBool:areAdsRemoved forKey:@"areAdsRemoved"];
//use NSUserDefaults so that you can load wether or not they bought it
[[NSUserDefaults standardUserDefaults] synchronize];
}

现在,在你的viewDidLoad方法的某个地方,你会想要添加以下代码:

areAdsRemoved = [[NSUserDefaults standardUserDefaults] boolForKey:@"areAdsRemoved"];
[[NSUserDefaults standardUserDefaults] synchronize];
//this will load wether or not they bought the in-app purchase


if(areAdsRemoved){
[self.view setBackgroundColor:[UIColor blueColor]];
//if they did buy it, set the background to blue, if your using the code above to set the background to blue, if your removing ads, your going to have to make your own code here
}

现在您已经添加了所有代码,进入.xibstoryboard文件,并添加两个按钮,一个表示购买,另一个表示恢复。将tapsRemoveAds IBAction连接到刚刚创建的购买按钮,将restore IBAction连接到还原按钮。restore动作将检查用户之前是否购买了应用内购买,如果他们还没有,则免费为他们提供应用内购买。

提交审查

接下来,进入App Store Connect,然后单击Users and Access,然后单击Sandbox Testers标头,然后单击左边的+符号,上面写着Testers。你可以随机输入名字和姓氏,电子邮件不必是真实的——你只需要能够记住它。输入密码(你必须记住),然后填写剩下的信息。我建议你将Date of Birth设置为一个使用户年满18岁的日期。App Store Territory 是在正确的国家。接下来,注销你现有的iTunes帐户(本教程结束后你可以重新登录)。

现在,在你的iOS设备上运行你的应用程序,如果你尝试在模拟器上运行它,购买将总是错误,你必须在你的iOS设备上运行它。应用程序运行后,点击购买按钮。当提示您登录iTunes帐户时,请以我们刚刚创建的测试用户登录。接下来,当它要求你确认购买99美分或任何你设置的价格层时,截取它的屏幕快照这是你将在App Store Connect上使用的screenshot for review。现在取消付款。

现在,转到App Store Connect,然后转到My Apps >the app you have the In-app purchase on祝辞In-App Purchases。然后点击你的应用内购买,点击应用内购买详情下的编辑。一旦你完成了这些,将你刚刚在iPhone上拍摄的照片导入到你的电脑中,并将其作为截图上传以供审查,然后,在审查笔记中,输入你的测试用户电子邮件和密码。这将有助于苹果在审查过程中。

完成此操作后,回到iOS设备上的应用程序,仍然以测试用户帐户登录,并单击购买按钮。在你确认付款之后,确保当用户购买你的产品时所发生的事情确实发生了。如果没有,那么这将是你的doRemoveAds方法的一个错误。同样,我建议使用将背景改为蓝色来测试应用内购买,但这不应该是你实际的应用内购买。如果一切正常,你就可以开始了!只要确保在你上传应用到App Store Connect时将应用内购买包含在你的新二进制文件中!


以下是一些常见错误:

记录: No Products Available

这可能意味着四件事:

  • 您没有在代码中放入正确的应用内购买ID(用于上面代码中的标识符kRemoveAdsProductIdentifier)
  • 你没有清除你在App Store Connect上出售的应用内购买
  • 你没有等待在App Store Connect中注册应用内购买ID。在创建ID后等待几个小时,您的问题就应该得到解决了。
  • 您没有完成协议、税务和银行信息的填写。

如果第一次没有成功,不要沮丧!不要放弃!我花了大约5个小时才让它工作,然后又花了大约10个小时寻找正确的代码!如果您准确地使用上面的代码,它应该可以正常工作。如果你有任何问题,请随意评论。

我希望这能够帮助到所有希望在iOS应用中添加应用内购买的人。干杯!

我知道我现在发表这篇文章有点晚了,但我在学习IAP模式时也分享了类似的经验。

应用内购买是由Storekit框架实现的iOS中最全面的工作流之一。整个文档是非常清楚的,如果你有耐心阅读它,但在技术上有点先进。

总结:

1 -请求产品-使用SKProductRequest &SKProductRequestDelegate类发出产品id请求,并从你自己的itunesconnect商店接收它们。

这些SKProducts应该用来填充您的商店UI,用户可以使用该UI购买特定的产品。

2 -发出付款请求-使用SKPayment &SKPaymentQueue将支付添加到事务队列。

3 -监视事务队列的状态更新-使用SKPaymentTransactionObserver协议的updatedTransactions方法来监视状态:

SKPaymentTransactionStatePurchasing - don't do anything
SKPaymentTransactionStatePurchased - unlock product, finish the transaction
SKPaymentTransactionStateFailed - show error, finish the transaction
SKPaymentTransactionStateRestored - unlock product, finish the transaction

4 -恢复按钮流-使用SKPaymentQueue的restoreCompletedTransactions来完成这一点-步骤3将照顾剩下的,以及SKPaymentTransactionObserver的以下方法:

paymentQueueRestoreCompletedTransactionsFinished
restoreCompletedTransactionsFailedWithError

在这里是一个循序渐进的教程(由我撰写,因为我自己试图理解它),解释它。最后还提供了可以直接使用的代码示例。

在这里是另一个我创建来解释某些事情,只有文本可以更好地描述。

RMStore是一个用于应用内购买的轻量级iOS库。它包装了StoreKit API,并为异步请求提供了方便的块。购买一个产品就像调用一个方法一样简单。

对于高级用户,这个库还提供了收据验证、内容下载和事务持久性。

只需将Jojodmo代码翻译成Swift:

class InAppPurchaseManager: NSObject , SKProductsRequestDelegate, SKPaymentTransactionObserver{










//If you have more than one in-app purchase, you can define both of
//of them here. So, for example, you could define both kRemoveAdsProductIdentifier
//and kBuyCurrencyProductIdentifier with their respective product ids
//
//for this example, we will only use one product


let kRemoveAdsProductIdentifier = "put your product id (the one that we just made in iTunesConnect) in here"


@IBAction func tapsRemoveAds() {


NSLog("User requests to remove ads")


if SKPaymentQueue.canMakePayments() {
NSLog("User can make payments")


//If you have more than one in-app purchase, and would like
//to have the user purchase a different product, simply define
//another function and replace kRemoveAdsProductIdentifier with
//the identifier for the other product
let set : Set<String> = [kRemoveAdsProductIdentifier]
let productsRequest = SKProductsRequest(productIdentifiers: set)
productsRequest.delegate = self
productsRequest.start()


}
else {
NSLog("User cannot make payments due to parental controls")
//this is called the user cannot make payments, most likely due to parental controls
}
}




func purchase(product : SKProduct) {


let payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(payment)
}


func restore() {
//this is called when the user restores purchases, you should hook this up to a button
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}




func doRemoveAds() {
//TODO: implement
}


/////////////////////////////////////////////////
//////////////// store delegate /////////////////
/////////////////////////////////////////////////
// MARK: - store delegate -




func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {


if let validProduct = response.products.first {
NSLog("Products Available!")
self.purchase(validProduct)
}
else {
NSLog("No products available")
//this is called if your product id is not valid, this shouldn't be called unless that happens.
}
}


func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {




NSLog("received restored transactions: \(queue.transactions.count)")
for transaction in queue.transactions {
if transaction.transactionState == .Restored {
//called when the user successfully restores a purchase
NSLog("Transaction state -> Restored")


//if you have more than one in-app purchase product,
//you restore the correct product for the identifier.
//For example, you could use
//if(productID == kRemoveAdsProductIdentifier)
//to get the product identifier for the
//restored purchases, you can use
//
//NSString *productID = transaction.payment.productIdentifier;
self.doRemoveAds()
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
break;
}
}
}




func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {


for transaction in transactions {
switch transaction.transactionState {
case .Purchasing: NSLog("Transaction state -> Purchasing")
//called when the user is in the process of purchasing, do not add any of your own code here.
case .Purchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
self.doRemoveAds() //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
NSLog("Transaction state -> Purchased")
case .Restored:
NSLog("Transaction state -> Restored")
//add the same code as you did from SKPaymentTransactionStatePurchased here
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
case .Failed:
//called when the transaction does not finish
if transaction.error?.code == SKErrorPaymentCancelled {
NSLog("Transaction state -> Cancelled")
//the user cancelled the payment ;(
}
SKPaymentQueue.defaultQueue().finishTransaction(transaction)
case .Deferred:
// The transaction is in the queue, but its final status is pending external action.
NSLog("Transaction state -> Deferred")


}




}
}
}

迅速的回答

这是为了补充Swift用户的我的Objective-C答案,以防止Objective-C答案变得太大。

设置

首先,在appstoreconnect.apple.com上设置应用内购买。按照我的Objective-C答案的开始部分(步骤1-13,在App Store Connect标题下)的指示来做。

你的产品ID可能需要几个小时才能在App Store Connect上注册,所以要有耐心。

现在你已经在App Store Connect上设置了你的应用内购买信息,我们需要将苹果的应用内购买框架StoreKit添加到应用中。

进入你的Xcode项目,进入应用程序管理器(左上角类似蓝色页面的图标,你的应用程序文件在那里)。点击左边目标下的应用程序(这应该是第一个选项),然后转到顶部的“功能”。在列表中,你应该看到一个选项“In-App Purchase”。打开这个功能,Xcode会将StoreKit添加到你的项目中。

编码

现在,我们要开始编码了!

首先,创建一个新的swift文件来管理你所有的应用内购买。我将它命名为IAPManager.swift

在这个文件中,我们将创建一个名为IAPManager的新类,它是SKProductsRequestDelegateSKPaymentTransactionObserver。在顶部,确保导入FoundationStoreKit

import Foundation
import StoreKit


public class IAPManager: NSObject, SKProductsRequestDelegate,
SKPaymentTransactionObserver {
}

接下来,我们将添加一个变量来定义应用内购买的标识符(你也可以使用enum,如果你有多个iap,这将更容易维护)。

// This should the ID of the in-app-purchase you made on AppStore Connect.
// if you have multiple IAPs, you'll need to store their identifiers in
// other variables, too (or, preferably in an enum).
let removeAdsID = "com.skiplit.removeAds"

接下来,让我们为我们的类添加一个初始化式:

// This is the initializer for your IAPManager class
//
// A better, and more scaleable way of doing this
// is to also accept a callback in the initializer, and call
// that callback in places like the paymentQueue function, and
// in all functions in this class, in place of calls to functions
// in RemoveAdsManager (you'll see those calls in the code below).


let productID: String
init(productID: String){
self.productID = productID
}

现在,我们将添加SKProductsRequestDelegateSKPaymentTransactionObserver工作所需的函数:

稍后我们将添加RemoveAdsManager

// This is called when a SKProductsRequest receives a response
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse){
// Let's try to get the first product from the response
// to the request
if let product = response.products.first{
// We were able to get the product! Make a new payment
// using this product
let payment = SKPayment(product: product)


// add the new payment to the queue
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
}
else{
// Something went wrong! It is likely that either
// the user doesn't have internet connection, or
// your product ID is wrong!
//
// Tell the user in requestFailed() by sending an alert,
// or something of the sort


RemoveAdsManager.removeAdsFailure()
}
}


// This is called when the user restores their IAP sucessfully
private func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue){
// For every transaction in the transaction queue...
for transaction in queue.transactions{
// If that transaction was restored
if transaction.transactionState == .restored{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier


// In this case, we have only one IAP, so we don't need to check
// what IAP it is. However, this is useful if you have multiple IAPs!
// You'll need to figure out which one was restored
if(productID.lowercased() == IAPManager.removeAdsID.lowercased()){
// Restore the user's purchases
RemoveAdsManager.restoreRemoveAdsSuccess()
}


// finish the payment
SKPaymentQueue.default().finishTransaction(transaction)
}
}
}


// This is called when the state of the IAP changes -- from purchasing to purchased, for example.
// This is where the magic happens :)
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
for transaction in transactions{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier


// In this case, we have only one IAP, so we don't need to check
// what IAP it is.
// However, if you have multiple IAPs, you'll need to use productID
// to check what functions you should run here!


switch transaction.transactionState{
case .purchasing:
// if the user is currently purchasing the IAP,
// we don't need to do anything.
//
// You could use this to show the user
// an activity indicator, or something like that
break
case .purchased:
// the user successfully purchased the IAP!
RemoveAdsManager.removeAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
// the user restored their IAP!
IAPTestingHandler.restoreRemoveAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
// The transaction failed!
RemoveAdsManager.removeAdsFailure()
// finish the transaction
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred:
// This happens when the IAP needs an external action
// in order to proceeded, like Ask to Buy
RemoveAdsManager.removeAdsDeferred()
break
}
}
}

现在让我们添加一些可以用来开始购买或恢复购买的函数:

// Call this when you want to begin a purchase
// for the productID you gave to the initializer
public func beginPurchase(){
// If the user can make payments
if SKPaymentQueue.canMakePayments(){
// Create a new request
let request = SKProductsRequest(productIdentifiers: [productID])
// Set the request delegate to self, so we receive a response
request.delegate = self
// start the request
request.start()
}
else{
// Otherwise, tell the user that
// they are not authorized to make payments,
// due to parental controls, etc
}
}


// Call this when you want to restore all purchases
// regardless of the productID you gave to the initializer
public func beginRestorePurchases(){
// restore purchases, and give responses to self
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}

接下来,让我们添加一个新的工具类来管理我们的iap。所有这些代码都可以在一个类中,但是有多个类会使它更简洁。我将创建一个名为RemoveAdsManager的新类,并在其中放入几个函数

public class RemoveAdsManager{


class func removeAds()
class func restoreRemoveAds()


class func areAdsRemoved() -> Bool


class func removeAdsSuccess()
class func restoreRemoveAdsSuccess()
class func removeAdsDeferred()
class func removeAdsFailure()
}

前三个函数,removeAdsrestoreRemoveAdsareAdsRemoved,是您将调用来执行某些操作的函数。后四个将被IAPManager调用。

让我们在前两个函数removeAdsrestoreRemoveAds中添加一些代码:

// Call this when the user wants
// to remove ads, like when they
// press a "remove ads" button
class func removeAds(){
// Before starting the purchase, you could tell the
// user that their purchase is happening, maybe with
// an activity indicator


let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginPurchase()
}


// Call this when the user wants
// to restore their IAP purchases,
// like when they press a "restore
// purchases" button.
class func restoreRemoveAds(){
// Before starting the purchase, you could tell the
// user that the restore action is happening, maybe with
// an activity indicator


let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginRestorePurchases()
}

最后,让我们在最后五个函数中添加一些代码。

// Call this to check whether or not
// ads are removed. You can use the
// result of this to hide or show
// ads
class func areAdsRemoved() -> Bool{
// This is the code that is run to check
// if the user has the IAP.


return UserDefaults.standard.bool(forKey: "RemoveAdsPurchased")
}


// This will be called by IAPManager
// when the user sucessfully purchases
// the IAP
class func removeAdsSuccess(){
// This is the code that is run to actually
// give the IAP to the user!
//
// I'm using UserDefaults in this example,
// but you may want to use Keychain,
// or some other method, as UserDefaults
// can be modified by users using their
// computer, if they know how to, more
// easily than Keychain


UserDefaults.standard.set(true, forKey: "RemoveAdsPurchased")
UserDefaults.standard.synchronize()
}


// This will be called by IAPManager
// when the user sucessfully restores
//  their purchases
class func restoreRemoveAdsSuccess(){
// Give the user their IAP back! Likely all you'll need to
// do is call the same function you call when a user
// sucessfully completes their purchase. In this case, removeAdsSuccess()


removeAdsSuccess()
}


// This will be called by IAPManager
// when the IAP failed
class func removeAdsFailure(){
// Send the user a message explaining that the IAP
// failed for some reason, and to try again later
}


// This will be called by IAPManager
// when the IAP gets deferred.
class func removeAdsDeferred(){
// Send the user a message explaining that the IAP
// was deferred, and pending an external action, like
// Ask to Buy.
}

把所有这些放在一起,我们得到了这样的结果:

import Foundation
import StoreKit


public class RemoveAdsManager{


// Call this when the user wants
// to remove ads, like when they
// press a "remove ads" button
class func removeAds(){
// Before starting the purchase, you could tell the
// user that their purchase is happening, maybe with
// an activity indicator


let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginPurchase()
}


// Call this when the user wants
// to restore their IAP purchases,
// like when they press a "restore
// purchases" button.
class func restoreRemoveAds(){
// Before starting the purchase, you could tell the
// user that the restore action is happening, maybe with
// an activity indicator


let iap = IAPManager(productID: IAPManager.removeAdsID)
iap.beginRestorePurchases()
}


// Call this to check whether or not
// ads are removed. You can use the
// result of this to hide or show
// ads
class func areAdsRemoved() -> Bool{
// This is the code that is run to check
// if the user has the IAP.


return UserDefaults.standard.bool(forKey: "RemoveAdsPurchased")
}


// This will be called by IAPManager
// when the user sucessfully purchases
// the IAP
class func removeAdsSuccess(){
// This is the code that is run to actually
// give the IAP to the user!
//
// I'm using UserDefaults in this example,
// but you may want to use Keychain,
// or some other method, as UserDefaults
// can be modified by users using their
// computer, if they know how to, more
// easily than Keychain


UserDefaults.standard.set(true, forKey: "RemoveAdsPurchased")
UserDefaults.standard.synchronize()
}


// This will be called by IAPManager
// when the user sucessfully restores
//  their purchases
class func restoreRemoveAdsSuccess(){
// Give the user their IAP back! Likely all you'll need to
// do is call the same function you call when a user
// sucessfully completes their purchase. In this case, removeAdsSuccess()
removeAdsSuccess()
}


// This will be called by IAPManager
// when the IAP failed
class func removeAdsFailure(){
// Send the user a message explaining that the IAP
// failed for some reason, and to try again later
}


// This will be called by IAPManager
// when the IAP gets deferred.
class func removeAdsDeferred(){
// Send the user a message explaining that the IAP
// was deferred, and pending an external action, like
// Ask to Buy.
}


}


public class IAPManager: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver{


// This should the ID of the in-app-purchase you made on AppStore Connect.
// if you have multiple IAPs, you'll need to store their identifiers in
// other variables, too (or, preferably in an enum).
static let removeAdsID = "com.skiplit.removeAds"


// This is the initializer for your IAPManager class
//
// An alternative, and more scaleable way of doing this
// is to also accept a callback in the initializer, and call
// that callback in places like the paymentQueue function, and
// in all functions in this class, in place of calls to functions
// in RemoveAdsManager.
let productID: String
init(productID: String){
self.productID = productID
}


// Call this when you want to begin a purchase
// for the productID you gave to the initializer
public func beginPurchase(){
// If the user can make payments
if SKPaymentQueue.canMakePayments(){
// Create a new request
let request = SKProductsRequest(productIdentifiers: [productID])
request.delegate = self
request.start()
}
else{
// Otherwise, tell the user that
// they are not authorized to make payments,
// due to parental controls, etc
}
}


// Call this when you want to restore all purchases
// regardless of the productID you gave to the initializer
public func beginRestorePurchases(){
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}


// This is called when a SKProductsRequest receives a response
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse){
// Let's try to get the first product from the response
// to the request
if let product = response.products.first{
// We were able to get the product! Make a new payment
// using this product
let payment = SKPayment(product: product)


// add the new payment to the queue
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
}
else{
// Something went wrong! It is likely that either
// the user doesn't have internet connection, or
// your product ID is wrong!
//
// Tell the user in requestFailed() by sending an alert,
// or something of the sort


RemoveAdsManager.removeAdsFailure()
}
}


// This is called when the user restores their IAP sucessfully
private func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue){
// For every transaction in the transaction queue...
for transaction in queue.transactions{
// If that transaction was restored
if transaction.transactionState == .restored{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier


// In this case, we have only one IAP, so we don't need to check
// what IAP it is. However, this is useful if you have multiple IAPs!
// You'll need to figure out which one was restored
if(productID.lowercased() == IAPManager.removeAdsID.lowercased()){
// Restore the user's purchases
RemoveAdsManager.restoreRemoveAdsSuccess()
}


// finish the payment
SKPaymentQueue.default().finishTransaction(transaction)
}
}
}


// This is called when the state of the IAP changes -- from purchasing to purchased, for example.
// This is where the magic happens :)
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]){
for transaction in transactions{
// get the producted ID from the transaction
let productID = transaction.payment.productIdentifier


// In this case, we have only one IAP, so we don't need to check
// what IAP it is.
// However, if you have multiple IAPs, you'll need to use productID
// to check what functions you should run here!


switch transaction.transactionState{
case .purchasing:
// if the user is currently purchasing the IAP,
// we don't need to do anything.
//
// You could use this to show the user
// an activity indicator, or something like that
break
case .purchased:
// the user sucessfully purchased the IAP!
RemoveAdsManager.removeAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
// the user restored their IAP!
RemoveAdsManager.restoreRemoveAdsSuccess()
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
// The transaction failed!
RemoveAdsManager.removeAdsFailure()
// finish the transaction
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred:
// This happens when the IAP needs an external action
// in order to proceeded, like Ask to Buy
RemoveAdsManager.removeAdsDeferred()
break
}
}
}


}

最后,你需要添加一些方法,让用户开始购买并调用RemoveAdsManager.removeAds(),开始恢复并调用RemoveAdsManager.restoreRemoveAds(),就像某个按钮!请记住,根据App Store指南,你需要提供一个按钮来恢复购买。

提交审查

最后要做的是将你的IAP提交到App Store Connect进行审查!关于这方面的详细说明,你可以参考我的Objective-C答案的最后一部分,在提交审查标题下。